快速排序算法 & 查找第k大的数

快速排序算法

部分排序与快速排序算法

快速排序含有“二分”的思想,通过递归调用部分排序,大幅度减少比较次数,使得元素之间不需要两两进行比较,但也因此无法保证稳定性。

快速排序算法希望能够选取元素集合中的一个值(一般是首元素)作为基准值,并将它移到一个合适的位置,使得集合中比基准值小的元素都位于基准值左侧,而比不小于基准值的元素都位于基准值的右侧。(将等于基准值的元素划分于基准值的左侧或是右侧都是可以的)
这样,原本无序的集合就变成了 相对有序的两个部分 (但此时这两个部分内部仍然是无序的)。接着就是对每个部分再进行排序,这就刚刚的做法一样了,选取基准值,整理集合,使其成为相对有序的两个部分。
这是一个不断二分的过程,当相对有序的两个部分都只含有一个元素时,即完成二分,就代表某一段已经完全有序。当所有二分工作都完成时,就得到了完整的有序集合。

// 部分排序算法
int partition_sort(vector<int>& nums, int i, int j) {
    int key = nums[i];
    while (i < j) {
        // 从右到左找到第一个不大于key的值
        while (i < j && nums[j] > key) j--;
        nums[i] = nums[j];

        // 从左到右找到第一个大于key的值
        while (i < j && nums[i] <= key) i++;
        nums[j] = nums[i];
    }
    nums[i] = key;
    return i;  // 返回key的位置
}

// 快速排序算法
void quick_sort(vector<int>& nums, int beg, int end) {
    if (beg < end) {
        int pivot = partition_sort(nums, beg, end);
        quick_sort(nums, beg, pivot - 1);
        quick_sort(nums, pivot + 1, end);
    }
}

查找第k大的数

因为我们实际要找的是第k大的数,那么就只需要保证 比倒数第k个位置上的元素值大的元素都位于此位置的右侧 (这实际上就是基准值的定义)即可。如果将先将集合整体排好序,再选出第k大的数,就会存在不必要的排序过程。
当我们利用部分排序完成对集合的“二分”时,我们可以通过下标来判断第k大的数会落在哪里。如果第k大的元素刚好落在基准值上,那么基准值值的元素值就是我们要找的第k大的元素。如果落在基准值左侧,那么下一步就对左侧的部分进行部分排序。如果落于右侧,则对右侧的部分进行部分排序。
根据我们的目标 【比倒数第k个位置上的元素值大的元素都位于此位置的右侧】,我们希望第k大的数刚好就是基准值。所以,当第k大的数落在基准值上时,我们就找到了答案。

// 该函数需要调用部分排序算法
int quick_select(vector<int>& nums, int begin, int end, int k) {
    if (k > nums.size()) return -1;
    int pivot;
    while (true) {
        pivot = partition_sort(nums, begin, end);
        if (nums.size() - pivot == k) return nums[pivot];
        else if (nums.size() - pivot < k) end = pivot - 1;  // 要找的数在 pivot 左边
        else begin = pivot + 1;  // 要找的数在 pivot 右面
    }
}

LeetCode链接:https://leetcode-cn.com/problems/kth-largest-element-in-an-array

posted @ 2022-04-18 10:33  ZenonX  阅读(276)  评论(0编辑  收藏  举报