-
数据结构-堆(Heap)
-
我认识的堆:
1.建立在完全二叉树的基础上
2.排序算法的一种,也是稳定效率最高的一种
3.可用于实现STL中的优先队列(priority_queue)
优先队列:一种特殊的队列,队列中元素出栈的顺序是按照元素的优先权大小,而不是元素入队的先后顺序
4.两类:
a.最大堆:
①根的值大于左右子树的值 ②子树也是最大堆
b.最小堆:
①根的值小于左右子树的值 ②子树也是最小堆
堆的实现:
堆一般用数组表示
1.插入(Insert):
以最大堆为栗子,插入其实就是把插入结点放在堆最后面,然后与父亲比较,如果父亲值小于它,那么它就和父亲结点交换位置,重复该过程,直到插入节点遇到一个值比它大的父亲或者它成为树根结点
以最大堆为栗子,在堆中插入值为20的结点(红色结点代表新进入)
20明显大于它的父亲结点值,所以和7交换位置,交换后新的父亲值还是比它小,继续交换
一步一步与它前面比它小的父亲结点交换位置,最后20成为根结点
最小堆同理,只不过要求父亲结点要比它小,如果父亲结点大于它,就得交换
2.删除(Pop):
删除就是删除最大堆中的最大值或者最小堆中的最小值,也就是树根
以删除最大堆树根为例子,删除其实就是整个堆中少了一个结点,不妨把位于最后面的一个结点当成新的树根,然后与它左右孩子比较,与值最大并且值大于它的孩子进行交换(好好读这句话),直到它的孩子都是小于它的,或者变成树叶
还是用最大堆为例,删除树根20
把最后的结点8提到树根
然后就按说明步骤,找到一个位置,它的孩子都小于它或者他变成树叶
-
堆模板代码:
//最小堆
#define MAX_SIZE 100005
int Heap[MAX_SIZE];
int Cur = 0;
void Insert(int val) //插入
{
Heap[++Cur] = val; //新元素加入堆
int Temp_Cur = Cur;
while (Temp_Cur > 1) //新元素还没成为树根时
{
int Root = Temp_Cur / 2; //找爸爸
if (Heap[Root] > val) //爸爸比它大
swap(Heap[Root], Heap[Temp_Cur]); //交换
else
break; //它已经在堆中立足
Temp_Cur = Root; //该新结点的新位置
}
}
int Pop() //删除
{
if (Cur == 0) //堆空
return -99999999;
int Temp_Top = Heap[1]; //保存堆顶
Heap[1] = Heap[Cur]; //将末尾结点提到树根
int Root = 1;
while (2 * Root < Cur) //还没有变成树叶
{
int L_Child = Root * 2; //左儿子
int R_Child = Root * 2 + 1; //右儿子
if (R_Child >= Cur || Heap[L_Child] < Heap[R_Child]) //没有右儿子或者左儿子值小
{
if (Heap[Root] > Heap[L_Child]) //比最小的儿子大
{
swap(Heap[Root], Heap[L_Child]); //交换
Root = L_Child; //新位置
}
else
break;
}
else
{
if (Heap[Root] > Heap[R_Child])
{
swap(Heap[Root], Heap[R_Child]);
Root = R_Child;
}
else
break;
}
}
Cur--;
return Temp_Top; //返回堆顶
}
-
堆的用途:
高效排序:
对于许多acm题目来说超时很简单,所以一般能不用sort就别用,堆排序很快,在排序类,贪心类题目中会用到,不过不用自己写,STL中的queue提供了堆的模板--优先队列(priority_queue)
实现Huffman:
哈夫曼算法在构建哈夫曼树时每次必须把结点排序取最小的两个结点构建子树,这很好的用到了堆,还是因为快,自己上个月写了哈夫曼树的一篇blog用了sort,而且把Huffman树存在数组。。。好像是为了方便后面编码,到时候用优先队列和链表试着更新。
Huffman基础可以先了解: 数据结构-哈夫曼树(Huffman)
-
图片来源: