【LeetCode】排序数组——不一样的方式实现快排

目录

题目链接 

颜色分类

算法原理

代码实现

排序数组 

算法原理

代码实现

最小的k个数

算法原理

代码实现 


题目链接 

LeetCode链接:75. 颜色分类 - 力扣(LeetCode)

LeetCode链接:912. 排序数组 - 力扣(LeetCode)

LeetCode链接:面试题 17.14. 最小K个数 - 力扣(LeetCode)

颜色分类

算法原理

我们可以将这个数组划分为三个区域,左边区域全是0也就是红色,中间区域全是1也就是白色,右边区域全是2也就是蓝色。因此我们可以用个变量i来遍历数组,变量left标记0(红色)区域的最右侧,变量right标记2(蓝色)区域的最左侧。

那么就会形成下图所示区域

  • [0, left]:全都是0
  • [left + 1, i - 1]:全都是1
  • [i, right - 1]:全都是待遍历的元素
  • [right, n - 1]:全都是2

在遍历数组的时候分情况讨论

  • 当nums[i] == 0时:我们需要将 i 位置的元素和 left + 1位置的元素进行交换,这样就能保证在left的左边都是0(包括left),,交换完后i向后移动,swap(nums[++left], nums[i++]。
  • 当nums[i] == 1时:直接i++,这样就能保证left + 1到i这个区域内都是1。
  • 当nums[i] == 2时:我们需要将 i 位置的元素和 right - 1位置的元素进行交换,这样就能保证在right的右边都是2(包括right),此时的 i 不需要移动,因为 i 到 right - 1 的这块区域都是待遍历的元素,交换后还是待遍历的元素。swap(nums[--right], nums[i])。

当i >= right时停止遍历 

代码实现

class Solution {
public:
    void sortColors(vector<int>& nums) {
        int i = 0, n = nums.size();
        int left = -1, right = n;
        while(i < right)
        {
            if(nums[i] == 0)
            {
                swap(nums[++left], nums[i++]);
            }
            else if(nums[i] == 1)
            {
                i++;
            }
            else
            {
                swap(nums[--right], nums[i]);
            }
        }
    }
};

排序数组 

 

算法原理

 和上面颜色分类的思想是一样的,将数组划分为三块区域,左边区域为小于key的,中间区域为等于key的,右边区域为大于key的。

分类讨论:

  • 当nums[i] < key时:我们需要将 i 位置的元素和 left + 1 位置的元素进行交换,这样就能保证在left的左边全都是比key小的数(包括left),交换完后i向后移动,swap(nums[++left], nums[i++]。
  • 当nums[i] == key时:直接i++,这样就能保证left + 1到i这个区域内都是等于key的。
  • 当nums[i] > key时:我们需要将 i 位置的元素和 right - 1位置的元素进行交换,这样就能保证在right的右边都是大于key的(包括right),此时的 i 不需要移动,因为 i 到 right - 1 的这块区域都是待遍历的元素,交换后还是待遍历的元素。swap(nums[--right], nums[i])。

小优化:可以选择随机的方式来选择key值,至于为什么可以去看看算法导论这本书,里面给出了详细证明。 

代码实现

class Solution {
public:
    vector<int> sortArray(vector<int>& nums) {
        srand(time(nullptr));
        qsort(nums, 0, nums.size() - 1);
        return nums;
    }
    void qsort(vector<int>& nums, int l, int r)
    {
        if(l >= r) return;

        int key = getRandom(nums, l, r);//随机取key值
        //分三块区域
        int i = l, left = l - 1, right = r + 1;
        while(i < right)
        {
            if(nums[i] < key) swap(nums[++left], nums[i++]);
            else if(nums[i] == key) i++;
            else swap(nums[--right], nums[i]);
        }

        //递归
        qsort(nums, l, left);
        qsort(nums, right, r);
    }

    int getRandom(vector<int>& nums, int left, int right)
    {
        int r = rand();
        return nums[r % (right - left + 1) + left];
    }
};

最小的k个数

 

算法原理

 原理和上面的排序数组一样,只是在最后要进行讨论一下k的大小

分类讨论:

  • 如果c >= k:k是落在大于key的这个区域内,只需递归这个区域找出key即可。
  • 如果b + c >= k:直接返回key就行了,因为k就落在了等于key的这个区域内。
  • 如果上面两种情况都不满足,那说明k落在了小于key这个区域内,只需找k - b - c(要把前面两个的区域去掉)大的并且递归这个区域即可。

代码实现 

class Solution {
public:
    int getRandom(vector<int>& arr, int left, int right)
    {
        return arr[rand() % (right - left + 1) + left];
    }
    void qsort(vector<int>& arr, int l, int r, int k)
    {
        if(l >= r) return;

        int key = getRandom(arr, l, r);
        int i = l, left = l - 1, right = r + 1;
        while(i < right)
        {
            if(arr[i] < key) swap(arr[++left], arr[i++]);
            else if(arr[i] == key) i++;
            else swap(arr[--right], arr[i]);
        }

        int a = left - l + 1, b = right - left - 1;
        if(a >= k) qsort(arr, l, left, k);
        else if(a + b >= k) return;
        else qsort(arr, right, r, k - a - b);
    }
    vector<int> smallestK(vector<int>& arr, int k) {
        srand(time(nullptr));
        qsort(arr, 0, arr.size() - 1, k);
        return {arr.begin(), arr.begin() + k};
    }
};

今天的内容就分享到这里了,如果内容有错,有写的不好的地方,还望告知,谢谢!!!

  • 15
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值