目录
[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 的时候,说明已经将所有数据扫描完毕了),一直进行下面循环:
根据 numscur 的值,可以分为下面三种情况:
i. numscur == 0 ;说明此时这个位置的元素需要在 left + 1 的位置上,因此交换 left + 1 与 cur 位置的元素,并且让 left++ (指向 0 序列的右边界),cur++ (为什么可以++ 呢,是因为 left + 1 位置要么是 0 ,要么是 cur ,交换完毕之后,这个位置的值已经符合我们的要求,因此 cur++ );
ii。 numscur == 1 ;说明这个位置应该在 left 和 cur 之间,此时无需交换,直接让 cur++ ,判断下一个元素即可;
iii. numscur == 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<int>& 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]);
}
}
};

二、排序数组
2.1 题目

2.2 思路
我们在数据结构阶段学习的快速排序的思想可以知道,快排最核心的一步就是 Partition (分割数据):将数据按照一个标准,分成左右两部分。
如果我们使用荷兰国旗问题的思想,将数组划分为 左 中 右 三部分:左边是比基准元素小的数据,中间是与基准元素相同的数据,右边是比基准元素大的数据。然后再去递归的排序左边部分和右边部分即可(可以舍去大量的中间部分)。
在处理数据量有很多重复的情况下,效率会大大提升。
随机选择基准算法流程:
函数设计**:int randomKey(vector<int>& nums, int left, int right)**
a. 在主函数那里种一颗随机数种子;
b. 在随机选择基准函数这里生成一个随机数;
c. 由于我们要随机产生一个基准,因此可以将随机数转换成随机下标:让随机数 % 上区间大小,然后加上区间的左边界即可。

快速排序算法主要流程:
a. 定义递归出口;
b. 利用随机选择基准函数生成一个基准元素;
c. 利用荷兰国旗思想将数组划分成三个区域;
d. 递归处理左边区域和右边区域。
2.3 代码实现
cpp
class Solution {
public:
int getRandom(vector<int>& nums,int left,int right)
{
int a = rand();
return nums[a%(right-left+1)+left];
}
void qsort(vector<int>& 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<int> sortArray(vector<int>& nums)
{
srand(time(NULL)); //种下一颗随机种子
qsort(nums,0,nums.size()-1);
return nums;
}
};

三、数组中的第K个最大元素(快速选择算法)
3.1 题目

3.2 思路
在快排中,当我们把数组「分成三块」之后: l, left left + 1, right - 1
right, r ,我们可以通过计算每一个区间内元素的「个数」,进而推断出我们要找的元素是在「哪一个区间」里面。
那么我们可以直接去「相应的区间」去寻找最终结果就好了

3.3 代码实现
cpp
class Solution {
public:
int getRandom(vector<int>& nums,int l,int r)
{
int a =rand();
return nums[a%(r-l+1)+l];
}
int qsort(vector<int>& 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<int>& nums, int k)
{
srand(time(NULL));
return qsort(nums,0,nums.size()-1,k);
}
};

四、LCR 159. 库存管理 III(最小的k个数)
4.1 题目

4.2 思路
这道题可以用 排序 O(N logN)、堆 O(Nlogk)、快速选择算法 O(N)
快速选择算法:
在快排中,当我们把数组「分成三块」之后: l, left,left + 1, right - 1,right, r ,我们可以通过计算每一个区间内元素的「个数」,进而推断出最小的 k 个数在哪些区间里面。
那么我们可以直接去「相应的区间」继续划分数组即可。

4.3 代码实现
cpp
class Solution {
public:
int getRandom(vector<int>& nums,int l,int r)
{
return nums[rand()%(r-l+1)+l];
}
void qsort(vector<int>& 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<int> inventoryManagement(vector<int>& nums, int k)
{
srand(time(NULL));
qsort(nums,0,nums.size()-1,k);
return {nums.begin(),nums.begin()+k};
}
};

关于随机基准元素,在算法导论中有严谨的证明,感兴趣可以去看下。
本篇完!
