C++大小顶堆实现、堆排序以及priority_queue

本文介绍了堆的数据结构定义,包括大顶堆和小顶堆的特点,以及如何通过遍历和调整构造堆。还讨论了堆排序算法和C++中的优先队列(priority_queue)在解决特定LeetCode问题中的应用,如977.有序数组的平方和347.前K个高频元素问题。
摘要由CSDN通过智能技术生成

最近在刷leetcode和学习数据结构,针对堆的问题写一篇文章进行总结,方便之后自己回顾,如果有些的不对的地方请帮忙批评指针!感谢。

堆的定义

对于一个数组或者是一个序列如nums[],满足下面的性质:

  1. 如果对于nums中的每一个元素都满足nums[i]>=nums[2i]且nums[i]>=nums[2i+1](1<=i<=n/2),这里n表示nums元素个数,i表示nums第i个元素下标这里i从1开始(在代码中会从0开始),则这个数据结构称为大顶堆。
  2. 如果对于nums中的每一个元素都满足nums[i]<=nums[2i]且nums[i]<=nums[2i+1](1<=i<=n/2),这里n表示nums元素个数,i表示nums第i个元素下标这里i从1开始(在代码中会从0开始),则这个数据结构称为小顶堆。
  3. 这里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);
    }
}

这里对应的是力扣的一道排序题可以练一下手:

977. 有序数组的平方 - 力扣(LeetCode)

priority_queue

347. 前 K 个高频元素 - 力扣(LeetCode)对于求这种题目一般都是用大小顶堆的思想,但是自己实现一个优点麻烦,c++中其实有一个数据结构其底层就是大小顶堆来实现的就是优先队列priority_queue!

优先队列查找一个队列中的最大值或者最小值的时间复杂度为以O(log n) ,其中是最大值还是最小值是根据创建的优先队列的性质来决定的。

优先队列有三个参数,其声明形式为:
priority_queue< type, container, function >
  1. type:数据类型;
  2. container:实现优先队列的底层容器;
  3. 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)

一篇学完!王道考研408数据结构(全) - 知乎 (zhihu.com)

代码随想录 (programmercarl.com)

  • 9
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

永不秃头的三三

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值