目錄
1.C++中的堆實現
可以直接用優先級隊列priority_queue
默認是大頂堆 priority_queue<int> maxheap
小頂堆 priority_queue<int,vector<int>,greater<int>> minheap
也可以使用 multiset (使用multiset的情況一般爲 需要從堆中刪除元素,因爲priority_queue的方法中沒有 erase方法)
默認是大頂堆 multiset<int> maxheap
小頂堆 multiset<int,greater<int>> minheap
2.單堆問題
指通過一個堆就可以解決的問題
一般這種問題都具有以下特點:
求解第/前 k個最大,最小或是最頻繁的元素;都可以使用堆來實現 (而不用通過排序實現)
模式:
確定大頂堆還是小頂堆
比如求 第K個最大元素,我們就用 大小爲K的小頂堆,遍歷數組完畢後,小頂堆堆頂元素即爲第K個最大元素
遍歷數組,壓入小頂堆,判斷小頂堆的元素個數,如果大於k,則彈出,保證小頂堆內元素個數始終是 k個
對於最頻繁或是最不頻繁的元素問題:可以首先結合 pair<int,int>對組 通過遍歷統計,然後以 對組爲 堆中元素進行 入堆
priority_queue<pair<int,int>,vector<pair<int,int>>,less<pair<int,int>>> maxheap
比如:尋找第k個最大元素
求第k個最大元素,我們保留下來前k個最大元素即可,而前k個元素中我們又要最小的一個,所以我們可以使用小頂堆
保證小頂堆元素內個數不超過k即可,因爲 隨着我們壓入元素,堆的大小增大,而小元素 一定在堆頂,所以 當堆的大小超過k時,就會 pop出去小元素,最後堆中剩下前k個大元素
class Solution {
public:
int findKthLargest(vector<int>& nums, int k) {
for(auto i : nums)
{
minheap.push(i);
if(minheap.size() > k)
{
minheap.pop();
}
}
return minheap.top();
}
private:
priority_queue<int,vector<int>,greater<int>> minheap;
};
class Solution {
public:
vector<int> topKFrequent(vector<int>& nums, int k)
{
unordered_map<int,int> map;
vector<int> res;
for(int i = 0; i < nums.size(); i++)//統計每個元素的出現的頻率
{
map[nums[i]]++;
}
priority_queue<pair<int,int>,vector<pair<int,int>>,less<pair<int,int>>> pq; //用大頂堆來記錄 頻率和對應元素,
//用pair的第一個元素代表頻率,第二個元素代表對應該頻率對應的元素
for(auto it = map.begin(); it != map.end(); it++)
{
pq.push(make_pair(it->second,it->first));
if(pq.size() > map.size()-k)
{
res.push_back(pq.top().second);
pq.pop();
}
}
return res;
}
};
3.雙堆問題
指通過兩個堆相互配合解決問題
特點:
被告知,我們拿到一大把可以分成兩隊的數字。怎麼把數字分成兩半?使得:小的數字都放在一起,大的放在另外一半。雙堆模式就能高效解決此類問題。然後通過小頂堆尋找最小數據,大頂堆尋找堆中最大數據
這樣中位數就可以通過 小頂堆和大頂堆堆頂元素求出
模式:
比如求 數據流中的 中位數,因爲 數據流一直在增加,所以如果採用排序,那麼每一次增加元素後均需要再次排序,時間複雜度過高; 由於中位數 只需要知道中間兩個數(或一個數)即可求出
我們可以使用 大頂堆 來存儲 數據流中的 一半較小元素;用 小頂堆來存儲 數據流中的 一般較大元素;
且保證 大頂堆的大小 大於 等於 小頂堆的大小,這樣 如果 數據流大小爲奇數,則返回 大頂堆 的堆頂元素即可,如果數據流大小爲偶數,則 大頂堆和小頂堆元素取平均即可
如何實現?
//已知此時數據流元素個數爲2k,大頂堆中存儲較小元素,大頂堆存儲較大元素
maxheap.push(val);//此時堆頂爲大元素
將大元素 壓入 小頂堆中
minheap.push(maxheap.top())
maxheap.pop()//從大頂堆中刪除
//且要保證 大頂堆的大小不小於 小頂堆大小
while(maxheap.size() < minheap.size()) {將 小頂堆中元素壓入大頂堆}
//最後通過驗證數據流大小 來求中位數即可
class MedianFinder {
public:
/** initialize your data structure here. */
MedianFinder() {
}
void addNum(int num) {
maxheap.push(num);//將所有數壓入大頂堆
minheap.push(maxheap.top());// 將 大頂堆中的最大元素壓入最小堆
maxheap.pop();
//同時要保持,maxheap的大小比 minheap大或者相等
if(maxheap.size() < minheap.size())
{
maxheap.push(minheap.top());
minheap.pop();
}
}
double findMedian() {
if(minheap.size() != maxheap.size())
{
return maxheap.top();
}
return (minheap.top()+maxheap.top())*0.5;
}
private:
priority_queue<int> maxheap;//大頂堆 ,裝 小一部分的數,始終保持大頂堆中多一個數
priority_queue<int,vector<int>,greater<int>> minheap;//小頂堆,裝 比較大一部分的數
};
/**
* Your MedianFinder object will be instantiated and called as such:
* MedianFinder* obj = new MedianFinder();
* obj->addNum(num);
* double param_2 = obj->findMedian();
*/
這裏由於是滑動窗口,也就是說 兩個堆裏面的總元素是有刪減的,所以不能再用 priority_queue,因爲其中沒有 erase方法
我們可以使用 multiset來實現堆內元素的刪減
然後結合固定滑動窗口法即可(逐漸改變滑動窗口內元素即可)
詳細代碼如下:
class Solution {
public:
vector<double> medianSlidingWindow(vector<int>& nums, int k) {
vector<double> res;
for(int i = 0; i < k; i++)
{
maxheap.insert(nums[i]);
minheap.insert(*maxheap.begin());
maxheap.erase(maxheap.begin());
if(maxheap.size() < minheap.size())
{
maxheap.insert(*minheap.begin());
minheap.erase(minheap.begin());
}
}//初始的大頂堆與小頂堆
if(k%2 == 0)
{
res.push_back((*minheap.begin() * 0.5 +*maxheap.begin() *0.5));
}
else
{
res.push_back(*maxheap.begin());
}
//開始移動滑動窗口,並改變大小頂堆內元素
for(int i = 0; i < nums.size()-k; i++)
{
//刪除左窗口元素,因爲已經開始移動
if(maxheap.find(nums[i]) != maxheap.end())
{
maxheap.erase(maxheap.find(nums[i]));
}
else if(minheap.find(nums[i]) != minheap.end())
{
minheap.erase(minheap.find(nums[i]));
}
//開始添加一個右窗口元素
maxheap.insert(nums[i+k]);
minheap.insert(*maxheap.begin());
maxheap.erase(maxheap.begin());
while(maxheap.size() < minheap.size())
{
maxheap.insert(*minheap.begin());
minheap.erase(minheap.begin());
}
if(k%2 == 0)
{
res.push_back((*minheap.begin() * 0.5 +*maxheap.begin() *0.5));
}
else
{
res.push_back(*maxheap.begin());
}
}
return res;
}
private:
multiset<int> minheap;//升序,begin處最小
multiset<int,greater<int>> maxheap;//降序,begin()最大
};