首页 > 其他分享 >【LeetCode排序专题02】最小k个数,关于快速排序的讨论

【LeetCode排序专题02】最小k个数,关于快速排序的讨论

时间:2023-04-04 12:56:40浏览次数:33  
标签:02 arr int 数组 pivot 排序 LeetCode left

最小k个数

输入整数数组 arr ,找出其中最小的 k 个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。

示例 1:

输入:arr = [3,2,1], k = 2
输出:[1,2] 或者 [2,1]

示例 2:

输入:arr = [0,1,2,1], k = 1
输出:[0]

限制:

0 <= k <= arr.length <= 10000
0 <= arr[i] <= 10000

快速排序

快排是一种冒泡法的优化版本,逻辑上使用了分治思想,代码实现上使用了递归的方式,直接上例子来说

以下是一个无序数组,使用快排对其进行排序,核心代码逻辑如右侧所示

默认以left初始时指向的最左侧元素为基准值

基准值也称为pivot,即"轴"。不断移动左右指针,与轴比较,比轴大的放在轴的右边,比轴小的放在轴的左边

在这里,pivot = 2,即nums[0]

此时满足最外层循环条件,进入循环

大循环内还有两个循环,用于移动左右指针

当前右指针大于基准值pivot(5 > 2),移动right,到3处,还是大,继续移动

到0处不满足条件,执行下一个小循环

当前左指针等于基准值pivot(2 > 0),满足第二个小循环条件,移动left,继续执行后面的语句

此时,将左右指针指向的值进行交换

当前左右指针并没有相交,因此继续执行大循环内的两个小循环

(后面的过程同理,就不画图了)

右指针的值还是大于pivot,right左移

此时不满足条件,往后执行第二个小循环

左指针的值小于pivot(0 < 1),left右移

左右指针相交,大循环结束,此时两指针相交的位置就是当前pivot需要移动到的位置

如图所示,pivot(2)移动到相交处

此时,我们就以pivot为基准对数组进行了第一次划分

当前,pivot(2)左边的都是小于2的数,pivot(2)右边的都是大于2的数

屏幕截图 2023-04-04 002138

然后,对当前pivot划分的左右区间再次进行划分(方法相同)

这里就是快排需要使用递归的原因,我们需要不断的划分剩余的区间,直到最后排好序

下面来看代码实现

代码分析

凡是涉及递归的都可以按照三部曲来写

1、确认递归函数的参数和返回值

这里是对数组进行排序操作,不需要返回值;

输入参数为待排序的数组以及需要排序的区间

class Solution {
private:
    //对数组进行排序操作,不需要返回值,输入参数为待排序的数组以及需要排序的区间
    void quickSort(vector<int>& arr, int left, int right){
        
    }
public:
    vector<int> getLeastNumbers(vector<int>& arr, int k) {

    }
};

2、确定终止条件

终止条件肯定是左右指针相交

class Solution {
private:
    //对数组进行排序操作,不需要返回值,输入参数为待排序的数组以及需要排序的区间
    void quickSort(vector<int>& arr, int left, int right){
        //确定终止条件
        if(left > right) return;
    }
public:
    vector<int> getLeastNumbers(vector<int>& arr, int k) {

    }
};

3、处理单层递归逻辑

在代码实现时,我们可以直接将left指针指向的值视为pivot(也就是说不用单独定义一个pivot变量,一是影响性能,二是在交换变量时容易逻辑混乱)

循环之前,定义两个循环变量i、j分别初始化为left和right的值

class Solution {
private:
    //对数组进行排序操作,不需要返回值,输入参数为待排序的数组以及需要排序的区间
    void quickSort(vector<int>& arr, int left, int right){
        //确定终止条件
        if(left >= right) return;
        
        //处理单层逻辑
        //单独定义循环变量
        int i = left, j = right;
        // int pivot = left;//不用多余再定义一个变量
        while(i < j){
            //右指针指向的数要大于基准值的话就不用动,仅移动指针(此时,基准值由left充当)
            while(i < j && arr[j] >= arr[left]) j--;
            while(i < j && arr[i] <= arr[left]) i++;//同理
            swap(arr[i], arr[j]);//不满足上述条件就交换
            //将right指向的但是小的数放到pivot左边,将left指向的但是大的数放到pivot右边
        }//大循环结束,此时左右指针相交,把pivot移动到相交位置
        swap(arr[i], arr[left]);//写j也行
        
        //此时已经将数组分为左区间(小于pivot)和右区间(大于pivot)
        //调用递归对左右区间再次进行划分,直到排序完成
        quickSort(arr, left, i - 1);//左区间递归排序(左指针left重置为数组最左边元素)
        quickSort(arr, i + 1, right);//右区间递归(右指针right重置为数组最右边元素)
    }
public:
    vector<int> getLeastNumbers(vector<int>& arr, int k) {

    }
};

完整代码

class Solution {
private:
    //对数组进行排序操作,不需要返回值,输入参数为待排序的数组以及需要排序的区间
    void quickSort(vector<int>& arr, int left, int right){
        //确定终止条件
        if(left >= right) return;
        
        //处理单层逻辑
        int i = left, j = right;
        // int pivot = left;//不用多余再定义一个变量
        while(i < j){
            //右指针指向的数要大于基准值的话就不用动,仅移动指针(此时,基准值由left充当)
            while(i < j && arr[j] >= arr[left]) j--;
            while(i < j && arr[i] <= arr[left]) i++;//同理
            swap(arr[i], arr[j]);//不满足上述条件就交换
            //将right指向的但是小的数放到pivot左边,将left指向的但是大的数放到pivot右边
        }//大循环结束,此时左右指针相交,把pivot移动到相交位置
        swap(arr[i], arr[left]);//写j也行
        
        //此时已经将数组分为左区间(小于pivot)和右区间(大于pivot)
        //调用递归对左右区间再次进行划分,直到排序完成
        quickSort(arr, left, i - 1);//左区间递归排序(左指针left重置为数组最左边元素)
        quickSort(arr, i + 1, right);//右区间递归(右指针right重置为数组最右边元素)
    }
public:
    vector<int> getLeastNumbers(vector<int>& arr, int k) {
        //调用递归函数
        quickSort(arr, 0, arr.size() - 1);
        vector<int> res(arr.begin(), arr.begin() + k);//排序后,取数组中"前k大的元素"构成结果数组

        return res; 
    }
};
优化版

这里其实有个可以优化的点

在每次划分完毕后,基准值都会在arr[i]的位置(i为循环变量)

因为题目要求是:返回数组中“前k个最小的数”,只要返回就行,这些数有无顺序均可以

因此可以根据k与i的关系来决定是否继续排序

  • 若k < i,代表第 k + 1 小的数字在 左子数组 中,则递归左子数组
  • 若k > i,代表第 k + 1 小的数字在 右子数组 中,则递归右子数组
  • 若k = i,代表此时 arr[k] 即为第 k + 1小的数字,则直接返回数组前 k 个数字即可;

在代码上,需要把递归函数的返回值改为数组,因为要依据i与k的大小关系来决定递归左区间还是右区间,并且,k也需要作为参数输入到递归函数中

TBD

标签:02,arr,int,数组,pivot,排序,LeetCode,left
From: https://www.cnblogs.com/DAYceng/p/17285041.html

相关文章

  • [BUUCTF]PWN-bjdctf_2020_babyrop
    注意本题需要用到ROPgadget安装命令:sudoapt-getinstallpython-capstonegitclonehttps://github.com/JonathanSalwan/ROPgadget.gitcdROPgadgetsudopythonsetup.pyinstall以下是相关使用命令:命令: ROPgadget--binary文件名--only"pop|ret"|greprdi命令: R......
  • 联合省选2023
    火车站显然可行当且仅当轨道连续且存在以其为对应方向端点的轨道差分判定即可,时间复杂度为\(O(n+m)\)城市建造\(G-E'\)为\(|V'|\)个连通块当且仅当\(V'\)中的点两两不连通,则:\(E'\)为\(V'\)的导出子图(\(V'\)内部的边需被删除)对于路径\(a_{0},a_{1},...,a_{k}\),若\(a_{0}......
  • 代码随想录Day20-Leetcode654.最大二叉树,617.合并二叉树,700.二叉搜索树中的搜索,98.验
    654.最大二叉树题目链接:https://leetcode.cn/problems/maximum-binary-tree/基本的模拟思路很快/***Definitionforabinarytreenode.*functionTreeNode(val,left,right){*this.val=(val===undefined?0:val)*this.left=(left===undefined......
  • [BUUCTF]PWN-bjdctf_2020_babystack2
          这题比较简单,注意无符号字符串变为负数之后会发生溢出即可pro.symbols是输出函数地址的意思r.recvuntil的使用是接收到字符串为止,然后将接受的数据返回为什么会有两个payload是因为我想使用这种方式看看行不行为什么是0x10,是因为main函数里不能大于10......
  • 你到底值多少钱?2023打工人薪酬指南
    你到底值多少钱?2023打工人薪酬指南 大家好,我是王有志,欢迎和我聊技术,聊漂泊在外的生活。作为打工人,你最关心什么?技能,成长,发展还是薪酬?刚毕业时,我为了赢得面试官的好感,说了很多违心话,如:“工资不要紧,主要是想学习”,又或者是“我对贵司的这块技术非常感兴趣”。现在想想,呸!恶......
  • WT6020同步降压24V降5V6A
        WT6020是高效率的,利用抖动频率,平均电流模式架构的单片同步降压DC/DC转换器。具有出色的线路和负载调节能力,能够提供高达10A的连续负载。该器件在7V至30V的输入电压范围内工作,并提供1V至25V的可调输出电压。   WT6020具有短路和热保护电路,以提高系统可靠性。内部......
  • 2020 年百度之星·程序设计大赛 - 测试赛1001 度度熊保护村庄
    ProblemDescription哗啦啦村袭击了喵哈哈村!度度熊为了拯救喵哈哈村,带着自己的伙伴去救援喵哈哈村去了!度度熊与伙伴们很快的就过来占据了喵哈哈村的各个军事要地,牢牢的守住了喵哈哈村。但是度度熊发现,这是一场旷日持久的战斗,所以度度熊决定要以逸待劳,保存尽量多的体力,去迎战哗啦啦......
  • 数据解构之插入排序
    一、插入排序常见三种的排序交换、选择、插入。什么是插入排序?每一趟都要把待排序数放到有序区中合适的位置插入。我们以前是把数,把一个待排序数放到有序区的某一端,这是以前的排序方法。     现在不是了,现在是把待排序数放到有序区当中的合适的位置插入,要注意插入位置的确定......
  • 滑动窗口-leetcode344-反转字符出啊
    编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组s的形式给出。不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用O(1)的额外空间解决这一问题。示例1:输入:s=["h","e","l","l","o"]输出:["o","l","l","e","h"]示例......
  • 滑动窗口-leetcode-167-俩树之和
    以长度为2的整数数组[index1,index2]的形式返回这两个整数的下标index1和index2。你可以假设每个输入只对应唯一的答案,而且你不可以重复使用相同的元素。你所设计的解决方案必须只使用常量级的额外空间。示例1:输入:numbers=[2,7,11,15],target=9输出:[1,2]......