1. 题⽬链接:剑指Offer40.最⼩的k个数
2. 题⽬描述:
3. 解法(快速选择算法):
算法思路:
在快排中,当我们把数组「分成三块」之后: [l, left] [left + 1, right - 1] [right, r] ,我们可以通过计算每⼀个区间内元素的「个数」,进⽽推断出最⼩的k个数在哪 些区间⾥⾯。
那么我们可以直接去「相应的区间」继续划分数组即可。
C++算法代码:
class Solution
{
public:
vector<int>answer; //答案
void qsort(vector<int>& stock,int l,int r,int cnt)
{
//边界情况
if(l>=r)
{
return;
}
//随机关键字
int key=stock[rand()%(r-l+1)+l];
//三区域划分
int left=l-1,right=r+1,i=l;
while(i<right)
{
if(stock[i]<key)
{
swap(stock[i++],stock[++left]);
}
else if(stock[i]>key)
{
swap(stock[i],stock[--right]);
}
else
{
i++;
}
}
//判断
int a=left-l+1,b=right-left-1,c=r-right+1;
//当a部分大于cnt时,因为a部分还未排序,所以不能找到最小的cnt个元素
if(a>=cnt)
{
qsort(stock,l,left,cnt);
}
//当a+b部分大于cnt时,由于b部分的元素都是一样的,所以可以直接在前cnt位置取数据,不在乎顺序
else if(a+b>=cnt)
{
return ;
}
//当a+b部分小于cnt时,说明还需要在后面找一些元素
else
{
qsort(stock,right,r,cnt-a-b);
}
}
vector<int> inventoryManagement(vector<int>& stock, int cnt)
{
//随机数种子
srand(time(NULL));
qsort(stock,0,stock.size()-1,cnt);
return {stock.begin(),stock.begin()+cnt};
}
};
Java算法代码:
class Solution
{
public int[] getLeastNumbers(int[] nums, int k)
{
qsort(nums, 0, nums.length - 1, k);
int[] ret = new int[k];
for (int i = 0; i < k; i++)
ret[i] = nums[i];
return ret;
}
public void qsort(int[] nums, int l, int r, int k)
{
if (l >= r) return;
// 1. 随机选择⼀个基准元素 + 数组分三块
int key = nums[new Random().nextInt(r - l + 1) + l];
int left = l - 1, right = r + 1, i = l;
while (i < right)
{
if (nums[i] < key) swap(nums, ++left, i++);
else if (nums[i] == key) i++;
else swap(nums, --right, i);
}
// 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);
}
public void swap(int[] nums, int i, int j)
{
int t = nums[i];
nums[i] = nums[j];
nums[j] = t;
}
}