快速选择算法

快速选择算法

  • 快速选择算法
    • 快速选择算法错误示例
    • 快速选择算法正确示例


快速选择算法

快速选择算法错误示例

假定pivot选择5,快速选择第二大的数

选择前 3 1 2 5 6 4
选择后 3 1 2 4 6 5
3 1 2 5 4 6
  • 在程序运行过程中,i在5处判断失效,j在4处判断失效
  • 交换5和4后,先做i++与j—跳过该位置,最后i,j处并不是要找的值
  • 如果不先进行i++与j—,程序运行超时(如i与j的位置都是5,即都是去等号的时候发生死循环)
  • 如果在while(nums[i] < pivot)中改成≤ ,并不能确定5在左边还是在右边,最后不能确定要找的值在那边

本题题目为Leetcode215. 数组中的第K个最大元素

给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。

请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。

示例 1:

输入:[3,2,1,5,6,4], k = 2
输出: 5

示例 2:

输入:[3,2,3,1,2,4,5,5,6],k = 4
输出: 4

提示:

  • 1 <= k <= nums.length <= 105
  • 104 <= nums[i] <= 104
class Solution {
public:
    void quickselect(vector<int>& nums, int k, int l, int r)
    {
        int i = l - 1, j = r + 1, pivot = nums[l + r >> 1];
        while (i < j){
            do i++; while(nums[i] < pivot);
            do j--; while(nums[j] > pivot);
            if(i < j)swap(nums[i], nums[j]);
        }
        int sl = j - l + 1;
        if(sl == k) return;
        if(sl > k) quickselect(nums, k, l, j);
        if(sl < k) quickselect(nums, k-sl, j+1, r);
    }
    int findKthLargest(vector<int>& nums, int k) {
        quickselect(nums, nums.size() - k + 1, 0, nums.size() - 1);
        return nums[nums.size() - k];
    }
};

另外尝试版本,能通过上面不通过用例,但仍失败,由于i,j处并不是要找的值,发生死循环

class Solution {
public:
    void quickselect(vector<int>& nums, int k, int l, int r)
    {
        int i = l - 1, j = r + 1, pivot = nums[l + r >> 1];
        while (i < j){
            do i++; while(nums[i] < pivot);
            do j--; while(nums[j] > pivot);
            if(i < j)swap(nums[i], nums[j]);
        }
        int sl = j - l -1;
        if(sl == k) return;
        if(sl > k) quickselect(nums, k, l, j-1);
        if(sl < k) quickselect(nums, k- sl -1, j, r);
    }
    int findKthLargest(vector<int>& nums, int k) {
        quickselect(nums, nums.size() - k, 0, nums.size() - 1);
        return nums[nums.size() - k];
    }
};
class Solution {
public:
    void quickselect(vector<int>& nums, int k, int l, int r)
    {
        int i = l - 1, j = r + 1, pivot = nums[l + r >> 1];
        while (i < j){
            do i++; while(nums[i] < pivot);
            do j--; while(nums[j] > pivot);
            if(i < j)swap(nums[i], nums[j]);
        }
        if(j == k) return;
        if(j > k) quickselect(nums, k, l, j);
        if(j < k) quickselect(nums, k, j+1, r);
    }
    int findKthLargest(vector<int>& nums, int k) {
        quickselect(nums, nums.size() - k, 0, nums.size() - 1);
        return nums[nums.size() - k];
    }
};

快速选择算法正确示例

一定要循环到l==r

class Solution {
public:
    void quickselect(vector<int>& nums, int k, int l, int r)
    {
        if(l == r) return;
        int i = l - 1, j = r + 1, pivot = nums[l + r >> 1];
        while (i < j){
            do i++; while(nums[i] < pivot);
            do j--; while(nums[j] > pivot);
            if(i < j)swap(nums[i], nums[j]);
        }
        if(j >= k) quickselect(nums, k, l, j);
        if(j < k) quickselect(nums, k, j+1, r);
    }
    int findKthLargest(vector<int>& nums, int k) {
        quickselect(nums, nums.size() - k, 0, nums.size() - 1);
        return nums[nums.size() - k];
    }
};
  • 为什么是j≥k 而不是j≤k(经测试j≤k不行)
    • 由于j可能是等于k的,所以需要quickselect(nums, k, l, j)
    • 例如上表 pivot取到5分为以下两种情况
      • j或者i指向5且能进行swap,a[i]≥a[j]
        • a[i]>a[j],会在swap之后进行j—,即j会指向5前面
        • a[i]=a[j],会在swap之后进行j—,j会指向其中一个5前面(假定有多个5
      • 不能换了
        • j指向5

此版本可以使用i > k,使用i≥k会出问题

class Solution {
public:
    int quickselect(vector<int>& nums, int k, int l, int r)
    {
        if (l == r) return nums[k];
        int i = l - 1 , j = r + 1 , pivot = nums[r];
        while (i < j){
            do j--; while(nums[j] > pivot);
            do i++; while(nums[i] < pivot);
            if(i < j)swap(nums[i], nums[j]);
        }
        if(i > k) return quickselect(nums, k, l, i-1);
        return quickselect(nums, k, i, r);
    }
    int findKthLargest(vector<int>& nums, int k) {
        return quickselect(nums, nums.size() - k , 0, nums.size() - 1);
    }
};

ACwing版本修改

class Solution {
public:
    int quickselect(vector<int>& nums, int k, int l, int r)
    {
        if (l == r) return nums[l];
        int i = l - 1 , j = r + 1 , pivot = nums[l+r>>1];
        while (i < j){
            do i++; while(nums[i] < pivot);
            do j--; while(nums[j] > pivot);
            if(i < j)swap(nums[i], nums[j]);
        }
        int sl = j - l;
        if(sl >= k) return quickselect(nums, k, l, j);
        return quickselect(nums, k-sl-1, j+1, r);
    }
    int findKthLargest(vector<int>& nums, int k) {
        return quickselect(nums, nums.size() - k, 0, nums.size() - 1);
    }
};
class Solution {
public:
    int quickselect(vector<int>& nums, int k, int l, int r)
    {
        if (l == r) return nums[l];
        int i = l - 1 , j = r + 1 , pivot = nums[l+r>>1];
        while (i < j){
            do i++; while(nums[i] < pivot);
            do j--; while(nums[j] > pivot);
            if(i < j)swap(nums[i], nums[j]);
        }
        int sl = j - l + 1;
        if(sl >= k) return quickselect(nums, k, l, j);
        return quickselect(nums, k-sl, j+1, r);
    }
    int findKthLargest(vector<int>& nums, int k) {
        return quickselect(nums, nums.size() - k + 1, 0, nums.size() - 1);
    }
};

LeetCode 解法

class Solution {
public:
    int quickselect(vector<int>& nums, int k, int l, int r)
    {
        if (l == r) return nums[l];
        int i = l - 1 , j = r + 1 , pivot = nums[l+r>>1];
        while (i < j){
            do i++; while(nums[i] < pivot);
            do j--; while(nums[j] > pivot);
            if(i < j)swap(nums[i], nums[j]);
        }
        if(j >= k) return quickselect(nums, k, l, j);
        return quickselect(nums, k, j+1, r);
    }
    int findKthLargest(vector<int>& nums, int k) {
        return quickselect(nums, nums.size() - k  , 0, nums.size() - 1);
    }
};

总结:总体来说快速选择算法思想采用快速排序的分治思想,但是需要注意边界条件,以及最终返回值的选择

你可能感兴趣的:(算法与数据结构,算法,leetcode,数据结构)