数组第k大的元素
class Solution {
public:
int findKthLargest(vector<int>& nums, int k) {
priority_queue<int, vector<int>, greater<int>> que;
for(int i = 0; i < nums.size(); i++){
que.push(nums[i]);
if(que.size() > k){
que.pop();
}
}
return que.top();
}
};
快排法:
class Solution {
int adjustBase(vector<int>& nums, int l, int r){
int temp = nums[l];
while(l < r){
while(l < r && nums[r] <= temp) r--;
nums[l] = nums[r];
while(l < r && nums[l] >= temp) l++;
nums[r] = nums[l];
}
nums[r] = temp;
return l;
}
public:
int findKthLargest(vector<int>& nums, int k) {
int l = 0;
int r = nums.size() - 1;
while(1){
int t = adjustBase(nums, l, r);
if(t > k - 1) r = t - 1;
else if(t < k - 1) l = t + 1;
else return nums[t];
}
return 0;
}
};
最小k个数
题目链接
维护一个k大小的大根堆,将所有元素插入,堆中元素个数超过k时就将堆顶元素弹出,这样最后堆中所有元素即为最小k个数(因为大数会被优先弹出堆)
class Solution {
public:
vector<int> getLeastNumbers(vector<int>& arr, int k) {
vector<int> vec(k, 0);
if (k == 0) { // 排除 0 的情况
return vec;
}
priority_queue<int> Q;
for (int i = 0; i < k; ++i) {
Q.push(arr[i]);
}
for (int i = k; i < (int)arr.size(); ++i) {
if (Q.top() > arr[i]) {
Q.pop();
Q.push(arr[i]);
}
}
for (int i = 0; i < k; ++i) {
vec[i] = Q.top();
Q.pop();
}
return vec;
}
};
另一种基于快排的方法:
class Solution {
int adjustBase(vector<int>& nums, int l, int r){
int temp = nums[l];
while(l < r){
while(l < r && nums[r] >= temp) r--;
nums[l] = nums[r];
while(l < r && nums[l] <= temp) l++;
nums[r] = nums[l];
}
nums[r] = temp;
return l;
}
public:
vector<int> smallestK(vector<int>& arr, int k) {
if(arr.size() == 0) return {};
if(arr.size() <= k) return arr;
int i = 0;
int j = arr.size() - 1;
vector<int> res;
while(1){
int t = adjustBase(arr, i, j);
if(t > k ) j = t - 1;
else if(t < k) i = t + 1;
else{
res.assign(arr.begin(), arr.begin() + t);
break;
}
}
return res;
}
};
前k个高频单词
题目链接
找元素频率的问题,一般都用哈希来统计元素的频率
然后维护一个k大小的小根堆,将哈希中所有元素插入(哈希可以自动去重)
最后堆中元素即为前k高频的单词
pair作为一个类更推荐用emplace_back,有一部分性能提升
注意的是:C++的优先级队列自定义比较函数时,与sort等的自定义比较函数正好相反,比如,实现大根堆就需要用<小于号(理解上是数组递增为小根堆才对,其实不然)
class Solution {
public:
vector<string> topKFrequent(vector<string>& words, int k) {
unordered_map<string, int> cnt;
for (auto& word : words) {
cnt[word]++;
}
auto cmp = [](const pair<string, int>& a, const pair<string, int>& b) {
return a.second == b.second ? a.first < b.first : a.second > b.second;
};
priority_queue<pair<string, int>, vector<pair<string, int>>, decltype(cmp)> que(cmp);
for (auto& it : cnt) {
que.emplace(it);
if (que.size() > k) {
que.pop();
}
}
vector<string> ret(k);
for (int i = k - 1; i >= 0; i--) {
ret[i] = que.top().first;
que.pop();
}
return ret;
}
};
数据流的中位数
题目链接
维护两个堆,大根堆用来存储较小半部分元素,小根堆存储较大半部分元素
并且为了保证当总元素个数为奇数时,中位数始终在大根堆里面,因此,我们需要定义小根堆元素个数一旦超过大根堆元素个数就立刻调整,把小根堆元素堆顶元素加入大根堆中。同时,为了保持平衡,大根堆元素个数也要保证最多只比小根堆元素个数最多多一个。
class MedianFinder {
public:
/** initialize your data structure here. */
MedianFinder() {
}
priority_queue<int, vector<int>, greater<int>> up; //小顶堆
priority_queue<int> down; //大顶堆
void addNum(int num) {
if (down.empty() || num <= down.top()) down.push(num); //插入元素小于大顶堆堆顶,插入大顶堆中。
else up.push(num); //否则插入小顶堆中。
//调整大顶堆和小顶堆元素
if (down.size() == up.size() + 2) {//大顶堆元素个数大于小顶堆元素个数超过一个时进行调整。
up.push(down.top());
down.pop();
} else if (up.size() == down.size() + 1) {//小顶堆元素个数大于小顶堆元素个数一个时,就要进行调整,为了使中点保持在大顶堆上。
//始终保持左边大顶堆的元素个数比右边小顶堆的元素个数等于,或最多大于一个
down.push(up.top());
up.pop();
}
}
double findMedian() {
if ((down.size() + up.size()) % 2) return down.top(); //元素总数为奇数时。
return (down.top() + up.top()) / 2.0; //元素总数为偶数时。
}
};
/**
* Your MedianFinder object will be instantiated and called as such:
* MedianFinder* obj = new MedianFinder();
* obj->addNum(num);
* double param_2 = obj->findMedian();
*/