堆
堆的定义及性质
堆是一棵完全二叉树,以大顶堆为例,每个节点的值不小于其左右孩子节点。用数组实现堆,下标为i的节点其左右孩子节点的小标为2i和2i+1。
相关操作
建堆:从最后一个有孩子的节点开始,逆序枚举,每个节点向下调整
插入元素:将新增元素插入堆尾,向上调整
删除(堆顶)元素:将最后一个元素覆盖堆顶,从堆顶向下调整
#include <bits/stdc++.h>
using namespace std;
const int maxn = 100; // 堆的总容量
int n; // 堆内元素个数
vector<int>heap(100,0); // 以大顶堆为例
// 节点i的左右孩子节点下标为2i和2i+1
// 调整[low,high]范围内的节点
// 向下调整堆
void downAdjust(int low,int high)
{
int i = low, j = i*2;
while(j<=high)
{
// 如果右孩子存在且值大于左孩子
if(j+1<=high && heap[j+1]>heap[j])
{
// 记住较大孩子的下标
j = j+1;
}
// 如果孩子中的最大权值比欲调整的节点i要大,就交换
if(heap[j]>heap[i])
{
int tmp = heap[i];
heap[i] = heap[j];
heap[j] = tmp;
i = j;
j = i*2;
}
else break; // 孩子节点的值均比根小
}
}
// 在下标[low,high]范围内上调
void upAdjust(int low,int high)
{
int i = high, j = i/2;
while(j>=low)
{
// 大于根节点
if(heap[i]>heap[j])
{
int tmp = heap[i];
heap[i] = heap[j];
heap[j] = tmp;
i = j;
j = i/2;
}
else break; // 没有比根节点大
}
}
// 往堆内添加元素
void insert(int x)
{
heap[++n] = x;
upAdjust(1,n);
}
// 删除元素
void deleteTop()
{
// 最后一个元素覆盖堆顶,再向下调整
heap[1] = heap[n--];
downAdjust(1,n);
}
//排序
void HeapSort()
{
while(n>=1)
{
cout<<heap[1]<<" ";
deleteTop();
}
cout<<endl;
}
int main()
{
cin>>n;
for(int i=1;i<=n;++i)
cin>>heap[i];
//建堆,从最后一个有孩子的节点开始调整
for(int i=n/2;i>=1;--i)
{
downAdjust(i,n);
}
// 堆排序
HeapSort();
/////insert(100);
////HeapSort();
//deleteTop();
//HeapSort();
return 0;
}
具体例子
1.topK问题(找最大的k的元素--建小顶堆;最小的k个元素--建大顶堆)
class Solution {
public:
// 优先级队列自定义数据类型的比较
struct cmp{
// 重写仿函数
bool operator() (pair<int,int> a,pair<int,int> b)
{
return a.second>b.second; // 最小值优先
}
};
vector<int> topKFrequent(vector<int>& nums, int k) {
unordered_map<int,int>mp;
// 用map对每个出现的元素计数
for(int i=0;i<nums.size();++i)
mp[nums[i]]++;
// 小顶堆
priority_queue<pair<int,int>,vector<pair<int,int>>,cmp>Q;
for(auto item : mp)
{
// 如果堆内元素不足k个 则入堆
if(Q.size()<k)
{
Q.push(item);
}
// 当前元素的频次低于堆顶元素 则不入堆
else if(item.second>Q.top().second)
{
Q.pop();
Q.push(item);
}
}
vector<int>ans;
while(!Q.empty())
{
ans.push_back(Q.top().first);
Q.pop();
}
return ans;
}
};时间复杂度:O(nlogk) 空间复杂度:O(n)


查看6道真题和解析