例题: 最小K个数
网址: https://leetcode-cn.com/problems/smallest-k-lcci/
前沿: 寻找一组任意排序数的最小数问题,一般处理办法有下面三种。但是最近做了好几题这种了,推荐采用快排的思路,堆排序需要不断去存数据和取数据,尤其是存储的数据是非基本数据类型很麻烦,推荐快排的方法,快排相对堆排序既节约时间,也能够加速。
- 全排列,但是时间复杂度较高
- 堆排序,对数据采用堆存储k个数,控制最大堆或者最小堆,那么最后堆顶元素就是寻找元素
- 快速排序思想,
随便找个数,进行一轮快排
快排结束,进行数据划分,假设这个数属于第t个
t==k,那么返回前t个;
t > k,那么问题规模缩小为在前面t-1个数找k个;
t < k,说明已经确定前t个数属于前k个,但是第t+1到k这些数还没确定,那么问题规模缩小为从t+1到右边界找k-t个数
方法1:直接调用sort排序函数即可实现,话说C++第层的sort排序函数具体是什么排序算法呢?这一篇还可以:
https://blog.csdn.net/qq_35440678/article/details/80147601
方法2:堆排序
class Solution {
public:
vector<int> smallestK(vector<int>& arr, int k) {
//使用最大堆是k*logn,否则是k*n
priority_queue<int> p;
vector<int> res;
if(k==0){
return res;
}
for(int i=0;i<k;++i)
p.push(arr[i]);
for(int i=k;i<arr.size();++i){
if(p.top()>arr[i]){
p.pop();
p.push(arr[i]);
}
}
while(!p.empty()){
res.push_back(p.top());
p.pop();
}
return res;
}
};
注意点:对于堆排序解决这个问题,需要先将k个数据存储在堆中,然后在进行比较,注意理解逻辑。
方法3:快速排序思想
class Solution {
public:
vector<int> smallestK(vector<int>& arr, int k) {
vector<int> ans;
quickSort(arr, ans, k, 0, arr.size() - 1);
return ans;
}
void quickSort(vector<int>& arr, vector<int>& res, int k, int left, int right) {
//快排的实现方式有多种,我们选择了其中的一种
int start = left;
int end = right;
while (left < right) {
while (left < right && arr[right] >= arr[start]) {
right--;
}
while (left < right && arr[left] <= arr[start]) {
left++;
}
swap(arr, left, right);
}
swap(arr, left, start);
//注意这里,start是数组中元素的下标。在start之前的元素都是比start指向的元素小,
//后面的都是比他大。如果k==start,正好start之前的k个元素是我们要找的,也就是
//数组中最小的k个,如果k>start,说明前k个元素不够,我们还要往后再找找。如果
//k<start,说明前k个足够了,我们只需要在start之前找k个即可。
if (left > k) {
quickSort(arr, res, k, start, left - 1);
} else if (left < k) {
quickSort(arr, res, k, left + 1, end);
} else {
//取前面的k个即可
for (int m = 0; m < k; ++m) {
res.push_back(arr[m]);
}
}
}
//交换数组中两个元素的值
void swap(vector<int>& arr, int i, int j) {
if (i == j)
return;
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
};
下面给出一个方法三的快排的非递归实现版本,然后给出自己总是出错的地方,引以为戒。
class Solution {
public:
vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
//两种实现方法:分别是快排思路和map映射的思路
if(input.size()<k || k==0 || input.size()==0) return vector<int>();
int n = input.size();
int l = 0;
int r = n - 1;
while(l!=k){
// cout<<l<<" "<<r<<endl;
int start = l;
int end = r;
while(l<r){
while(l<r && input[r]>=input[start]) r--;
while(l<r && input[l]<=input[start]) l++;
swap(input[l], input[r]);
}
swap(input[l], input[start]);
if(l>k){
r = l-1; //这一行包括下面一行是给下次的左右边界进行赋值,但是刚开始的时候写反了,先进行l赋值,导致r一直出错
l = start; //后面谨记这个,对于需要修改的部分,一定先用值,再修改。
}else if(l<k){
r = end;
l = l+1;
}
}
vector<int> res(k);
for(int i=0;i<k;i++) res[i]= input[i];
return res;
}
};
这个是常用快速排序的模板
//快速排序
void quick_sort(int s[], int l, int r)
{
if (l < r)
{
//Swap(s[l], s[(l + r) / 2]); //将中间的这个数和第一个数交换 参见注1
int i = l, j = r, x = s[l];
while (i < j)
{
while(i < j && s[j] >= x) // 从右向左找第一个小于x的数
j--;
if(i < j)
s[i++] = s[j];
while(i < j && s[i] < x) // 从左向右找第一个大于等于x的数
i++;
if(i < j)
s[j--] = s[i];
}
s[i] = x;
quick_sort(s, l, i - 1); // 递归调用
quick_sort(s, i + 1, r);
}
}
虽然上面给出了快排的常用模板,但是我个人还是喜欢我给的方法三中的快排实现,确实精巧。