------------------------------2021/2/2------------------------------------------
二刷
两种办法都需要掌握;
两种方法的优劣性比较
看起来分治法的快速选择算法的时间、空间复杂度都优于使用堆的方法,但是要注意到快速选择算法的几点局限性:
一、算法需要修改原数组,如果原数组不能修改的话,还需要拷贝一份数组,空间复杂度就上去了。
二、 算法需要保存所有的数据。如果把数据看成输入流的话,使用堆的方法是来一个处理一个,不需要保存数据,只需要保存 k 个元素的最大堆。而快速选择的方法需要先保存下来所有的数据,再运行算法。当数据量非常大的时候,甚至内存都放不下的时候,就麻烦了。所以当数据量大的时候还是用基于堆的方法比较好。
快排的partition函数有很多细节,一定要记住模板
++l和–r对应初始值i / j + 1 、 l >= r结束 、最后交换i和r
int partition(vector<int> &arr, int i, int j) {
int l = i, r = j + 1;
int mark = arr[l];
while(1) {
while(++l <= j && arr[l] < mark);
while(--r >= i && arr[r] > mark);
if(l >= r) break;
swap(arr[l], arr[r]);
}
swap(arr[i],arr[r]);
}
return r;
};
许久没有写快排,之前默写过但细节已经忘记了,借这个机会再写了一次
题目描述
解法一、堆排序
建立优先队列,也就是c++的大根堆,先填充k个数进去,然后对剩下元素,如果它小于优先队列队首就删除队首放入元素,直到放完剩下所有元素
class Solution {
public:
vector<int> getLeastNumbers(vector<int>& arr, int k) {
if(arr.empty() || k == 0) return {};
vector<int> ans;
priority_queue<int> pq;
for(int i = 0; i < k; i++) pq.push(arr[i]);
for(int i = k; i < arr.size(); i++) {
if(pq.top() > arr[i]) {
pq.pop();
pq.push(arr[i]);
}
}
while(k --) {
ans.push_back(pq.top());
pq.pop();
}
return ans;
}
};
时间复杂度O(nlogn)
空间复杂度O(k)
解法二、快排
实际是快排的改进版本,
保留快排的partition函数,得到基准元素的下标m
- m == k -1
即包括下标内有k个数,直接返回数组前k个数。
- m < k - 1
第k小的元素大于基准元素,所以将基准元素右边的数组内的元素作为新的基准元素继续partition
- m > k - 1
第k小的元素小于基准元素,所以将基准元素左边的数组内的元素作为新的基准元素继续partition
//快排解法
class Solution {
public:
vector<int> getLeastNumbers(vector<int>& arr, int k) {
if(arr.empty() || k == 0) return {};
int len = arr.size() - 1;
return FindLeast(arr, 0, len, k - 1);//第k个数下标为k-1
}
vector<int> FindLeast(vector<int>& arr, int l, int h, int k) {
int m = partition(arr, l, h);
if(m == k) {
vector <int> ans (arr.begin(),arr.begin() + k + 1);
return ans;
}
if(m < k) return FindLeast(arr, m + 1, h, k);
return FindLeast(arr, l, m - 1, k);
}
int partition(vector<int> &arr, int l, int h) {
//l作为基准元素留在原地所以i设为l,++i从i+1开始
//j设为h+1,保证--h从h开始
int i = l, j = h + 1;
int pNum = arr[l];
while(true) {
while(++ i <= h && arr[i] < pNum);
while(--j >= l && arr[j] > pNum);
if(i >= j) break;
swap(arr[i], arr[j]);
}
swap(arr[l], arr[j]);
return j;
}
};
时间复杂度趋近O(n)
空间复杂度O(1)
快排的partition函数加深一下记忆
要注意l和r的起始元素 避免掉坑
int partition(vector<int> &arr, int l, int h) {
//l作为基准元素留在原地所以i设为l,++i从i+1开始
//j设为h+1,保证--h从h开始
int i = l, j = h + 1;
int pNum = arr[l];
while(true) {
while(++ i <= h && arr[i] < pNum);
while(--j >= l && arr[j] > pNum);
if(i >= j) break;
swap(arr[i], arr[j]);
}
swap(arr[l], arr[j]);
return j;
}
```