【C++研发面试笔记】8. 基本数据结构-二叉堆

【C++研发面试笔记】8. 基本数据结构-二叉堆

最大堆、最小堆分别指堆顶为最大或最小元素的堆,也叫大顶和小顶堆。堆是一种基本的抽象数据类型,一般用二叉树表示并且递归定义,堆顶为树的根,保证树或者子树的根永远比子节点大或者小。
堆的一个经典的实现是完全二叉树。这样实现的堆成为二叉堆。完全二叉树是增加了限定条件的二叉树。假设一个二叉树的深度为n。为了满足完全二叉树的要求,该二叉树的前n-1层必须填满,第n层也必须按照从左到右的顺序被填满。

8.1 STL中的 priority_queue

<queue>头文件中,还定义了另一个非常有用的模板类priority_queue(优先队列)。优先队列与队列的差别在于优先队列不是按照入队的顺序出队,而是按照队列中元素的优先权顺序出队(默认为大者优先,也可以通过指定算子来指定自己的优先顺序,最大堆和最小堆的实现)。
所以优先级队列是堆的一个实现,到底用最大还是最小堆要看实际情况和个人定义。C++的STL里面容器priority_queue实现优先级队列,默认是大顶堆。尽管名为优先队列,但堆并不是队列。
优先级队列并不是队列,在队列中,我们可以进行的限定操作是dequeue和enqueue。dequeue是按照进入队列的先后顺序来取出元素。而在堆中,我们不是按照元素进入队列的先后顺序取出元素的,而是按照元素的优先级取出元素。

8.1.1 定义

priority_queue 模板类有三个模板参数,第一个是元素类型,第二个容器类型,第三个是比较算子。其中后两个都可以省略,默认容器为vector,默认算子为less,即小的往前排,大的往后排(出队时序列尾的元素出队,也就是最大堆)。
定义priority_queue 对象的示例代码如下:

priority_queue<int> q1;
priority_queue< pair<int, int> > q2; // 注意在两个尖括号之间一定要留空格。
priority_queue<int, vector<int>, greater<int> > q3; // 定义升序队列
8.1.2 比较算子的使用

初学者在使用priority_queue 时,最困难的可能就是如何定义比较算子了。可以直接用STL 的less算子和greater,默认为使用less 算子,即小的往前排,大的先出队。
自定义方法:
1、操作符重载方法:

struct node
{
    friend bool operator< (node n1, node n2)
    {
        return n1.priority < n2.priority;
    }
    int priority;
    int value;
}; 
priority_queue<node> qn;

2、通过Struct结构

struct cmp
{
    bool operator()(const int &a,const int &b)
    {
        return a>b;//最大堆
        return a<b;//最小堆
    }
};
priority_queue< int, vector<int>, cmp> qn;
8.1.3 基本函数:
empty() //判断是否为空
size() //返回大小
top() //队顶(堆顶)
push() //插入元素
emplace() //构造并插入元素
pop() //删除队顶元素
swap() //交换 

8.2 二叉堆的实现

堆的主要操作是插入和删除最小元素(元素值本身为优先级键值,小元素享有高优先级)。在插入或者删除操作之后,我们必须保持该实现应有的性质:

  1. 完全二叉树
  2. 每个节点值都小于或等于它的子节点。
8.2.1 插入操作

在插入操作的时候,会破坏上述堆的性质,所以需要进行名为percolate_up的操作,以进行恢复。新插入的节点new放在完全二叉树最后的位置,再和父节点(位置为当前位置减去1再除以2取整(k-1)/2,比如第4个元素的父节点位置是1,第7个元素的父节点位置是3)比较。如果new节点比父节点小,那么交换两者。交换之后,继续和新的父节点比较…… 直到new节点不比父节点小,或者new节点成为根节点。这样得到的树,就恢复了堆的性质。

8.2.2 删除操作

删除操作只能删除根节点。根节点删除后,我们会有两个子树,我们需要基于它们重构堆。进行percolate_down的操作: 让最后一个节点last成为新的节点,从而构成一个新的二叉树。再将last节点不断的和子节点比较。如果last节点比两个子节点中大的那一个小,则和该子节点交换。直到last节点比任一子节点都小,或者last节点成为叶节点。
计算两个子节点的位置的公式:左子节点:2K+1、右子节点:2K+2

注:这里针对的是根节点为零的情况,若根为1,则左右分别为2K与2K+1。

下面是一个利用数组实现二叉堆的例子:
这里写图片描述

这篇博文是个人的学习笔记,内容许多来源于网络(包括CSDN、博客园及百度百科等),博主主要做了微不足道的整理工作。由于在做笔记的时候没有注明来源,所以如果有作者看到上述文字中有自己的原创内容,请私信本人修改或注明来源,非常感谢>_<

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值