您的位置:首页 > 理论基础 > 数据结构算法

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

2016-10-02 20:14 471 查看

【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 二叉堆的实现

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

完全二叉树

每个节点值都小于或等于它的子节点。

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、博客园及百度百科等),博主主要做了微不足道的整理工作。由于在做笔记的时候没有注明来源,所以如果有作者看到上述文字中有自己的原创内容,请私信本人修改或注明来源,非常感谢>_<
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息