快速排序以及 TopN 问题

快速排序

快速排序的划分函数

first element 划分

int Partition(std::vector<int> &data, int left, int right) {
    int ret = left;
    int pivot = data[left];
    while (left < right) {
        while (left < right && data[right] > pivot) {
            --right;
        }
        while (left < right && data[left] <= pivot) { // 把 pivot 归到左半部分
            ++left;
        }
        if (left < right) {
            swap(data[left], data[right]);
        }
    }
    swap(data[left], data[ret]);
    return left;
}

void QuickSort(std::vector<int> &data, int left, int right) {
    if (right - left > 0) {
        int i = Partition(data, left, right - 1);
        QuickSort(data, left, i);
        QuickSort(data, i + 1, right);
    }
}

快速排序的时间复杂度

在理想情况下,每次 partition 都能平分 n 个元素,需要 lg(n) 次的划分,每一次划分都会遍历所有元素,所以时间复杂度为 O(nlgn),在基本有序情况下 partition 函数的时间复杂度为 O(n),划分会执行接近 n 次,所以时间复杂度为 O(n^2)。

快速排序的稳定性

[1,4,2(1),3,6,2(2),5,2(3)]
我们选择第二个 2 也就是 2(2) 为基准,一次partition后,2(3)与 4 交换了位置,得到
[1,2(3),2(1),2(2),6,3,5,4]
可见是不稳定的

如果一个数字在待排序的列表中出现三次或以上,而这个数字在列表中出现的(非首次和末次)一次被选为基准(pivot)则结果肯定是不稳定的。因此,通过更改比较时 >= 符号为 > 符号是没有意义的,因为不稳定的根源在于基准的选取。
如果要得到稳定的快速排序,必须在选取基准时避免这种情况,而且在交换元素时要小心不要打乱相同元素的相对顺序。一种可选的做法是先记录相同元素的相对位置,再进行排序。这需要额外的空间开销。

TopN 问题

由快速排序的过程可知,每次 partition 调用结束后,都能确定出一个元素的位置。所以如果使用从小到大的快速排序,那么第 k 大数,就出现在 pos = len - k 下标位置。所以有如下代码:

class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        int res_pos = nums.size() - k;
        QuickSort(nums, 0, nums.size(), res_pos);
        return nums[res_pos];
    }

    int Partition(std::vector<int>& data, int left, int right) {
        int ret = left;
        int pivot = data[left];
        while (left < right) {
            while (left < right && data[right] > pivot) {
                --right;
            }
            while (left < right && data[left] <= pivot) {
                ++left;
            }
            if (left < right) {
                swap(data[left], data[right]);
            }
        }
        swap(data[left], data[ret]);
        return left;
    }

    void QuickSort(std::vector<int>& data, int left, int right, int k) {
        if (right - left > 0) {
            int i = Partition(data, left, right - 1);
            if (i == k) {
                return;
            }
            QuickSort(data, left, i, k);
            QuickSort(data, i + 1, right, k);
        }
    }
};

Reference

posted on 2023-06-14 14:51  LambdaQ  阅读(54)  评论(0编辑  收藏  举报