最近在刷leetcode和学习数据结构,针对堆的问题写一篇文章进行总结,方便之后自己回顾,如果有些的不对的地方请帮忙批评指针!感谢。
堆的定义
对于一个数组或者是一个序列如nums[],满足下面的性质:
- 如果对于nums中的每一个元素都满足nums[i]>=nums[2i]且nums[i]>=nums[2i+1](1<=i<=n/2),这里n表示nums元素个数,i表示nums第i个元素下标这里i从1开始(在代码中会从0开始),则这个数据结构称为大顶堆。
- 如果对于nums中的每一个元素都满足nums[i]<=nums[2i]且nums[i]<=nums[2i+1](1<=i<=n/2),这里n表示nums元素个数,i表示nums第i个元素下标这里i从1开始(在代码中会从0开始),则这个数据结构称为小顶堆。
- 这里i<=n/2的原因是堆其实是一个二叉树,针对第i个节点他的左孩子和右孩子下标分别是2*i和2*i+1,而i>n/2部分表示没有孩子是叶子结点。
堆的构造
根据上面的思想就可以来构造堆了。以大顶堆为例,为了确保分支节点都满足nums[i]>=nums[2i]且nums[i]>=nums[2i+1],就需要比较分支节点和其子节点的大小,遍历的时候只要遍历分支节点因为同时也会比较叶子节点。
此外,为了不破坏已经比较结束的节点应该要从下往上进行比较。
这里以构造大顶堆的代码为例:
void BuildHeapMax(vector<int>& nums,int len){
//这里的第一个元素下标为0进行考虑
for(int i=(len-1)/2;i>=0;i--){
adjustMaxTop(nums,i,len);
}
}
比较节点函数,这里需要注意的是当元素互换的过程破坏了下一级堆则需要采用相同的方法继续往下调节:
void adjustMaxTop(vector<int>& nums,int k,int len){
//暂存比较的节点值
int tmp=nums[k];
//由于元素从0开始,那么i位置的左右孩子分别为2*i+1和2*i+2
for(int i=2*k+1;i<len;i=2*i+1){
//取左右孩子中最大的
if(i<len-1&&nums[i]<nums[i+1]){
i++;
}
//节点已经是最大直接break
if(tmp>=nums[i]){
break;
}else{
//否则把大的孩子赋值到第k个位置
//但是这时候会出现对于那个交换的子节点看不满足大顶堆的条件了
//需要继续比较,这个过程称为下坠
nums[k]=nums[i];
//k记录最后满足条件的位置
k=i;
}
}
//在满足大顶堆条件的位置将最先的暂存值赋值
nums[k]=tmp;
}
堆排序
堆排序 类似于选择排序,将堆顶的最大元素放入数据nums的末尾,而除去末尾的会继续构建大顶堆,直到获取递增序列
void HeapSort(vector<int>& nums,int len){
BuildHeapMax(nums,len);
for(int i=len-1;i>0;i--){
swap(nums[i],nums[0]);
adjustMaxTop(nums,0,i);
}
}
这里对应的是力扣的一道排序题可以练一下手:
priority_queue
347. 前 K 个高频元素 - 力扣(LeetCode)对于求这种题目一般都是用大小顶堆的思想,但是自己实现一个优点麻烦,c++中其实有一个数据结构其底层就是大小顶堆来实现的就是优先队列priority_queue!
优先队列查找一个队列中的最大值或者最小值的时间复杂度为以O(log n) ,其中是最大值还是最小值是根据创建的优先队列的性质来决定的。
优先队列有三个参数,其声明形式为:
priority_queue< type, container, function >
- type:数据类型;
- container:实现优先队列的底层容器;
- function:元素之间的比较方式;可以用仿函数实现
priority_queue成员函数
- bool empty() const:返回值为true,说明队列为空;
- int size() const:返回优先队列中元素的数量;
- void pop():删除队列顶部的元素,也即根节点
- int top():返回队列中的顶部元素,但不删除该元素;
- void push(int arg):将元素arg插入到队列之中;
针对这道力扣题就可以用这个数据结构解决,此外由于是前k个所以时间复杂度是 O(nlog k)
class Solution {
public:
class SmallMinHeap{
public:
bool operator()(const pair<int,int> &num1,const pair<int,int> &num2){
return num1.second>num2.second;
}
};
vector<int> topKFrequent(vector<int>& nums, int k) {
unordered_map<int,int> count_map;
for(int i=0;i<nums.size();i++){
count_map[nums[i]]++;
}
priority_queue<pair<int,int>,vector<pair<int,int>>,SmallMinHeap> pq;
for(auto& i:count_map){
//cout<<i.first<<" "<<i.second<<endl;
pq.push(i);
if(pq.size()>k){
pq.pop();
}
}
vector<int> result(k);
for (int i = k - 1; i >= 0; i--) {
result[i] = pq.top().first;
pq.pop();
}
return result;
}
};
end..
如有侵权必删。
参考:
数据结构|C++优先队列(priority_queue)用法详解 - 知乎 (zhihu.com)