算法专题(七):分治-快排

目录

一、颜色分类

[1.1 题目](#1.1 题目)

[1.2 思路](#1.2 思路)

[1.3 代码实现](#1.3 代码实现)

二、排序数组

[2.1 题目](#2.1 题目)

[2.2 思路](#2.2 思路)

[2.3 代码实现](#2.3 代码实现)

[三、数组中的第K个最大元素 (快速选择算法)](#三、数组中的第K个最大元素 (快速选择算法))

[3.1 题目](#3.1 题目)

[3.2 思路](#3.2 思路)

[3.3 代码实现](#3.3 代码实现)

[四、LCR 159. 库存管理 III (最小的k个数)](#四、LCR 159. 库存管理 III (最小的k个数))

[4.1 题目](#4.1 题目)

[4.2 思路](#4.2 思路)

[4.3 代码实现](#4.3 代码实现)


每道题目的大标题均是该题传送门!

个人主页: uyeonashi-CSDN博客

一、颜色分类

1.1 题目

1.2 思路

类比数组分两块的算法思想,这里是将数组分成三块,那么我们可以再添加一个指针,实现数组分三块。

设数组大小为 n ,定义三个指针 left, cur, right :

◦ left :用来标记 0 序列的末尾,因此初始化为 -1 ;

◦ cur :用来扫描数组,初始化为 0 ;

◦ right :用来标记 2 序列的起始位置,因此初始化为 n 。

在 cur 往后扫描的过程中,保证:

◦ [0, left] 内的元素都是 0 ;

◦ [left + 1, cur - 1] 内的元素都是 1 ;

◦ [cur, right - 1] 内的元素是待定元素;

◦ [right, n] 内的元素都是 2 。

a. 初始化 cur = 0,left = -1, right = numsSize ;

b. 当 cur < right 的时候(因为 right 表示的是 2 序列的左边界,因此当 cur 碰到right 的时候,说明已经将所有数据扫描完毕了),一直进行下面循环:

根据 nums[cur] 的值,可以分为下面三种情况:

i. nums[cur] == 0 ;说明此时这个位置的元素需要在 left + 1 的位置上,因此交换 left + 1 与 cur 位置的元素,并且让 left++ (指向 0 序列的右边界),cur++ (为什么可以++ 呢,是因为 left + 1 位置要么是 0 ,要么是 cur ,交换完毕之后,这个位置的值已经符合我们的要求,因此 cur++ );

ii。 nums[cur] == 1 ;说明这个位置应该在 left 和 cur 之间,此时无需交换,直接让 cur++ ,判断下一个元素即可;

iii. nums[cur] == 2 ;说明这个位置的元素应该在 right - 1 的位置,因此交换right - 1 与 cur 位置的元素,并且让 right-- (指向 2 序列的左边界),cur 不变(因为交换过来的数是没有被判断过的,因此需要在下轮循环中判断)

c. 当循环结束之后:

0, left\] 表示 0 序列; \[left + 1, right - 1\] 表示 1 序列; \[right, numsSize - 1\] 表示 2 序列。 #### 1.3 代码实现 ```cpp class Solution { public: void sortColors(vector& nums) { int n = nums.size(); int left = -1,cur = 0,right = n; while(cur < right) { if(nums[cur] == 0) swap(nums[++left],nums[cur++]); else if(nums[cur] == 1) cur++; else swap(nums[--right],nums[cur]); } } }; ``` ![](https://i-blog.csdnimg.cn/direct/83f57914c3c14d0b8d171f3137450a5a.png) ## 二、[排序数组](https://leetcode.cn/problems/sort-an-array/description/ "排序数组 ") ### 2.1 题目 ![](https://i-blog.csdnimg.cn/direct/d16273e9790d441ea29cad998072a5f2.png) ### 2.2 思路 我们在数据结构阶段学习的快速排序的思想可以知道,快排最核心的一步就是 Partition (分割数据):将数据按照一个标准,分成左右两部分。 如果我们使用荷兰国旗问题的思想,将数组划分为 左 中 右 三部分:左边是比基准元素小的数据,中间是与基准元素相同的数据,右边是比基准元素大的数据。然后再去递归的排序左边部分和右边部分即可(可以舍去大量的中间部分)。 **在处理数据量有很多重复的情况下,效率会大大提升。** **随机选择基准算法流程:** 函数设计**:int randomKey(vector\\& nums, int left, int right)** a. 在主函数那里种一颗随机数种子; b. 在随机选择基准函数这里生成一个随机数; c. 由于我们要随机产生一个基准,因此可以将随机数转换成随机下标:让随机数 % 上区间大小,然后加上区间的左边界即可。 ![](https://i-blog.csdnimg.cn/direct/6a00336a2cb74bd39bc6f3b40267edc9.png) **快速排序算法主要流程:** a. 定义递归出口; b. 利用随机选择基准函数生成一个基准元素; c. 利用荷兰国旗思想将数组划分成三个区域; d. 递归处理左边区域和右边区域。 ### 2.3 代码实现 ```cpp class Solution { public: int getRandom(vector& nums,int left,int right) { int a = rand(); return nums[a%(right-left+1)+left]; } void qsort(vector& nums,int l, int r) { if(l >= r) return; //数组分三块 int key = getRandom(nums,l,r); 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]); } //[l,left] [left+1,right-1] [right,r] qsort(nums,l,left); qsort(nums,right,r); } vector sortArray(vector& nums) { srand(time(NULL)); //种下一颗随机种子 qsort(nums,0,nums.size()-1); return nums; } }; ``` ![](https://i-blog.csdnimg.cn/direct/7d649a608c034c259313a4bc8453a082.png) ## 三、[数组中的第K个最大元素](https://leetcode.cn/problems/kth-largest-element-in-an-array/description/ "数组中的第K个最大元素 ")(快速选择算法) ### 3.1 题目 ![](https://i-blog.csdnimg.cn/direct/82c9c9fa1a3c4e6bbf0e7ff10a1c803e.png) ### 3.2 思路 在快排中,当我们把数组「分成三块」之后: \[l, left\] \[left + 1, right - 1

right, r\] ,我们可以通过计算每一个区间内元素的「个数」,进而推断出我们要找的元素是在「哪一个区间」里面。 那么我们可以直接去「相应的区间」去寻找最终结果就好了 ![](https://i-blog.csdnimg.cn/direct/648a4415300f4256ade41cbe90132d95.png) ### 3.3 代码实现 ```cpp class Solution { public: int getRandom(vector& nums,int l,int r) { int a =rand(); return nums[a%(r-l+1)+l]; } int qsort(vector& nums,int l,int r,int k) { if(l==r) return nums[l]; //随机选择基准元素 int key = getRandom(nums,l,r); //数组分三块 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]); } int b = right-left-1, c = r-right+1; if(c >= k) return qsort(nums,right,r,k); else if(b+c >= k) return key; else return qsort(nums,l,left,k-b-c); } int findKthLargest(vector& nums, int k) { srand(time(NULL)); return qsort(nums,0,nums.size()-1,k); } }; ``` ![](https://i-blog.csdnimg.cn/direct/d6c783240b5041b1b8ee4f7a85b3d729.png) ## 四、[LCR 159. 库存管理 III](https://leetcode.cn/problems/zui-xiao-de-kge-shu-lcof/description/ "LCR 159. 库存管理 III ")(最小的k个数) ### 4.1 题目 ![](https://i-blog.csdnimg.cn/direct/87dcd87822f44741b1e8067d34c40d12.png) ### 4.2 思路 这道题可以用 **排序 O(N logN)、堆 O(Nlogk)、快速选择算法 O(N)** 快速选择算法: 在快排中,当我们把数组「分成三块」之后: \[l, left\],\[left + 1, right - 1\],\[right, r\] ,我们可以通过计算每一个区间内元素的「个数」,进而推断出最小的 k 个数在哪些区间里面。 那么我们可以直接去「相应的区间」继续划分数组即可。 ![](https://i-blog.csdnimg.cn/direct/f2845b4885944276be5fe639f6868ce0.png) ### 4.3 代码实现 ```cpp class Solution { public: int getRandom(vector& nums,int l,int r) { return nums[rand()%(r-l+1)+l]; } void qsort(vector& nums,int l,int r, int k) { if(l == r) return; // 1. 随机选择一个基准元素 + 数组分三块 int key = getRandom(nums,l,r); 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]); } // [l, left][left + 1, right - 1] [right, r] // 2. 分情况讨论 int a = left-l+1, b = right -left -1; if(a > k) qsort(nums,l,left,k); else if( a+b >= k) return; else qsort(nums,right,r,k-a-b); } vector inventoryManagement(vector& nums, int k) { srand(time(NULL)); qsort(nums,0,nums.size()-1,k); return {nums.begin(),nums.begin()+k}; } }; ``` ![](https://i-blog.csdnimg.cn/direct/a9680af11a794fe8b0fa81d75a24e4c0.png) *** ** * ** *** 关于随机基准元素,在算法导论中有严谨的证明,感兴趣可以去看下。 本篇完! ![](https://i-blog.csdnimg.cn/direct/32f93091b1584ef5bf8a85d1cc3bc9cb.jpeg)

相关推荐
liang_2026几秒前
2.27省选模拟赛补题记录:直径(容斥,树形dp,换根dp)
笔记·学习·算法
pianmian110 分钟前
python每日十题(9)
数据结构·算法
努力学习的小廉12 分钟前
【C++】 —— 笔试刷题day_7
开发语言·c++
wuqingshun31415918 分钟前
蓝桥杯 整数变换
数据结构·c++·算法·职场和发展·蓝桥杯
大法师安东尼ds18 分钟前
unordered_map
算法·哈希算法
BS_Li35 分钟前
顺序表和链表
数据结构·链表·顺序表
姜行运1 小时前
数据结构入门【算法复杂度】
android·c语言·数据结构·算法
禁默1 小时前
C++11之深度理解lambda表达式
开发语言·c++
wen__xvn1 小时前
每日一题洛谷P8717 [蓝桥杯 2020 省 AB2] 成绩分析c++
c++·算法·蓝桥杯
Tadecanlan1 小时前
[C++面试] 你了解transform吗?
开发语言·c++