二叉堆(binary heap)

 堆(heap)
亦被称为:优先队列(priority
queue)
,是计算机科学中一类特殊的数据结构的统称。堆通常是一个可以被看做一棵树的数组对象。在队列中,调度程序反复提取队列中第一个作业并运行,因而实际情况中某些时间较短的任务将等待很长时间才能结束,或者某些不短小,但具有重要性的作业,同样应当具有优先权。堆即为解决此类问题设计的一种数据结构。

本文地址:http://www.cnblogs.com/archimedes/p/binary-heap.html,转载请注明源地址。

逻辑定义

n个元素序列{k1,k2...ki...kn},当且仅当满足下列关系时称之为堆:
(ki <=
k2i, ki <=
k2i+1)或者(ki >= k2i,
ki >= k2i+1),   (i = 1,2,3,4...n/2)

二叉堆一般用数组来表示。如果根节点在数组中的位置是1,第n个位置的子节点分别在2n
2n+1。因此,第1个位置的子节点在2和3,第2个位置的子节点在4和5。以此类推。这种基于1的数组存储方式便于寻找父节点和子节点。

如果存储数组的下标基于0,那么下标为i的节点的子节点是2i + 1与2i + 2;其父节点的下标是?(i ? 1) ∕ 2?。

如下图的两个堆:

        1               11
/ \ / 2 3 9 10
/ \ / \ / \ / 4 5 6 7 5 6 7 8
/ \ / \ / \ / 8 9 10 11 1 2 3 4

将这两个堆保存在以1开始的数组中:

位置:  1  2  3  4  5  6  7  8  9 10 11
左图: 1 2 3 4 5 6 7 8 9 10 11
右图: 11 9 10 5 6 7 8 1 2 3 4

性质

二叉堆是完全二叉树或者是近似完全二叉树。

二叉堆满足二个特性:

1.父结点的键值总是大于或等于(小于或等于)任何一个子节点的键值。

2.每个结点的左子树和右子树都是一个二叉堆(都是最大堆或最小堆)。

当父结点的键值总是大于或等于任何一个子节点的键值时为最大堆。当父结点的键值总是小于或等于任何一个子节点的键值时为最小堆

 
          

最小堆
                     
                     
              最大堆

堆支持以下的基本操作:

  • build:建立一个空堆;

  • insert:向堆中插入一个新元素;

  • update:将新元素提升使其符合堆的性质;

  • get:获取当前堆顶元素的值;

  • delete:删除堆顶元素;

  • heapify:使删除堆顶元素的堆再次成为堆。

某些堆实现还支持其他的一些操作,如斐波那契堆支持检查一个堆中是否存在某个元素。

基本操作实现:


1、删除优先级最高的元素(DeleteMin)

此操作分3步:

(1)直接删除根

(2)用最后一个元素代替根

(3)将堆向下重新调整

  输出堆顶元素之后,以堆中最后一个元素替代之;然后将根结点值与左、右子树的根结点值进行比较,并与其中小者进行交换;重复上述操作,直到是叶子结点或其关键字值小于等于左、右子树的关键字的值,将得到新的堆。称这个从堆顶至叶子的调整过程为“筛选”

调整堆算法实现如下:


void down(int p)  /* 调整堆算法 */
{
int q = p * 2; /* 向下调整算法,p代表当前结点,q代表子结点 */
a = heap[p]; /* 保存当前结点的值 */
while(q <= hlength) { /* hlength为堆中元素的个数 */
/* 选择两个子节点中的一个最小的 */
if(q < hlength && heap[q] > heap[q + 1])
q++;
if(heap[q] >= a) { /* 如果当前结点比子节点小,就结束*/
break;
} else { /* 否则就交换 */
heap[p] = heap[q];
p = q;
q = p * 2;
}
}
heap[p] = a; /* 安排原来的结点 */
}

删除最小元素,返回该最小元素:


int DeleteMin()
{
/* 删除最小元素算法 */
int r = heap[1]; /* 取出最小元素 */
heap[1] = heap[hlength--]; /* 把最后一个叶子结点赋值给根结点 */
down(1); /* 调用向下调整算法 */
return r;
}

2、在堆中插入一个新元素Insert(x):

向上调整算法:


void up(int p)
{
/* 向上调整算法,p代表当前结点,q代表父结点 */
int q = p / 2; /* 获取当前结点的父结点 */
a = heap[p]; /* 保存当前结点的值 */
while(q >= 1 && a < heap[q]) {
heap[p] = heap[q]; /* 如果当前结点的值比父母结点的值小,就交换 */
p = q;
q = p / 2;
}
heap[p] = a; /* 安排原来的结点 */
}

插入元素算法:


void insert(int a)
{
heap[++hlength] = a; /* 先往堆里插入结点值 */
up(hlength); /* 向上调整 */
}

3、将x的优先级上升为p:


void IncreaseKey(int a, int p)  /* 把p的优先级升为a */
{
if(heap[p] < a)
return;
heap[p] = a;
up(p);
}

4、建立堆:


void bulid()  /* 建堆算法 */
{
int i;
for(i = hlength / 2; i > 0; i--)
down(i); /* 从最后一个非终端结点开始进行调整 */
}

编程实践

poj2051 Argus

http://poj.org/problem?id=2051


#include<stdio.h>
#include<string.h>
char str[10];
typedef struct Node {
int now, Q, P;
}Node;
Node node[3001];
void down(Node H[], int s, int m)
{
int j;
Node rc = H[s];
for(j = s * 2; j <= m; j *= 2) {
if(j < m) {
if(H[j].now > H[j + 1].now) {
j++;
} else {
if((H[j].now == H[j + 1].now) && (H[j].Q > H[j + 1].Q))
j++;
}
}
if(rc.now < H[j].now || (rc.now == H[j].now && rc.Q < H[j].Q))
break;
H[s] = H[j];
s = j;
}
H[s] = rc;
}

void BulidMinHeap(Node H[], int length)
{
int i;
for(i = length / 2; i > 0; i--)
down(H, i, length);
}

void solve()
{
int i, len, K;
i = 1;
while(scanf("%s", str) && str[0] == ‘R‘) {
scanf("%d %d", &node[i].Q, &node[i].P);
getchar();
node[i].now = node[i].P;
i++;
}
len = i - 1;
scanf("%d", &K);
BulidMinHeap(node, len);
for(i = 1; i <= K; i++) {
printf("%d\n", node[1].Q);
node[1].now += node[1].P;
down(node, 1, len);
}
}
int main()
{
solve();
return 0;
}

参考资料


1、Thomas H. Cormen, Charles E. Leiserson, Ronald
L. Rivest, Clifford Stein(潘金贵等译)《算法导论》. 机械工业出版社.

2、Vuillemin, J. (1978). A data structure for manipulating priority
queues.
Communications of the ACM21, 309–314.

3、ACM/ICPC 算法训练教程

4、《数据结构》严蔚敏、吴伟民

5、维基百科

二叉堆(binary heap),布布扣,bubuko.com

时间: 2024-12-26 21:49:19

二叉堆(binary heap)的相关文章

数据结构 之 二叉堆(Heap)

注:本节主要讨论最大堆(最小堆同理). 一.堆的概念 堆,又称二叉堆.同二叉查找树一样,堆也有两个性质,即结构性和堆序性. 1.结构性质: 堆是一棵被全然填满的二叉树.有可能的例外是在底层.底层上的元素从左到右填入.这种树称为全然二叉树(complete binary tree).下图就是这样一个样例. 对于全然二叉树,有这样一些性质: (1).一棵高h的全然二叉树,其包括2^h ~ (2^(h+1) - 1)个节点.也就是说.全然二叉树的高是[logN],显然它是O(logN). (2).全然

堆(Heap)和二叉堆(Binary heap)

堆(Heap): The operations commonly performed with a heap are: create-heap: create an empty heap heapify: create a heap out of given array of elements find-max or find-min: find the maximum item of a max-heap or a minimum item of a min-heap (aka, peek)

ADT - Binary Heap(二叉堆) &amp;&amp;

今天早上起来完成了一个完整的基于二叉堆实现的优先队列,其中包含最小优先和最大优先队列. 实际上上篇也说了优先队列的特性,通过建堆和堆排序操作,我们就已经看到了这种数据结构中的数据具有某种优先级别,要么非根节点大于他的子节点,要么就相反,在最大优先队列中最大优先级别就是指节点值最大的数据为根节点,每次出队时肯定是最大的先出去,反之则是最小优先队列,但要注意插入时的数据不一定是最大或最小的,优先队列会通过一点小技巧找到所有节点之间的关系并对应起来,重新使得你随意插入的数据满足优先队列的特性,因而这种

在A*寻路中使用二叉堆

接上篇:A*寻路初探 GameDev.net 在A*寻路中使用二叉堆 作者:Patrick Lester(2003年4月11日更新) 译者:Panic 2005年3月28日 译者序:     这一篇文章,是"A* Pathfinding for Beginners.",也就是我翻译的另一篇文章<A*寻路初探>的补充,在这篇文章里,作者再一次展现了他阐述复杂话题的非凡能力,用通俗易懂的语句清晰的解释了容易让人迷惑的问题.还是那句话,如果你看了这篇文章仍然无法领会作者的意图,那

D&amp;F学数据结构系列——二叉堆

二叉堆(binary heap) 二叉堆数据结构是一种数组对象,它可以被视为一棵完全二叉树.同二叉查找树一样,堆也有两个性质,即结构性和堆序性.对于数组中任意位置i上的元素,其左儿子在位置2i上,右儿子在左儿子后的单元2i+1中,它的父亲在[i/2](向下取整)中. 因此,一个数据结构将由一个数组.一个代表最大值的整数.以及当前的堆的大小组成.一个典型的优先队列(priority queue)如下: 1 #ifndef _BinHeap_H 2 struct HeapStruct; 3 type

笔试算法题(46):简介 - 二叉堆 &amp; 二项树 &amp; 二项堆 &amp; 斐波那契堆

二叉堆(Binary Heap) 二叉堆是完全二叉树(或者近似完全二叉树):其满足堆的特性:父节点的值>=(<=)任何一个子节点的键值,并且每个左子树或者右子树都是一 个二叉堆(最小堆或者最大堆):一般使用数组构建二叉堆,对于array[i]而言,其左子节点为array[2*i],其右子节点为 array[2*i+1]:二叉堆支持插入,删除,查找最大(最小)键值的操作,但是合并二叉堆的复杂度较高,时间复杂度为O(N):但是二项堆或者斐波 那契堆则仅需要O(logN): 二项树(Binomial

《数据结构与算法分析:C语言描述》复习——第五章“堆”——二叉堆

2014.06.15 22:14 简介: 堆是一种非常实用的数据结构,其中以二叉堆最为常用.二叉堆可以看作一棵完全二叉树,每个节点的键值都大于(小于)其子节点,但左右孩子之间不需要有序.我们关心的通常只有堆顶的元素,而整个堆则被封装起来,保存在一个数组中. 图示: 下图是一个最大堆: 实现: 优先队列是STL中最常用的工具之一,许多算法的优化都要利用堆,使用的工具就是优先队列.STL中的优先队列通过仿函数来定义比较算法,此处我偷懒用了“<”运算符.关于使用仿函数的好处,我之后如果有时间深入学习S

数据结构 二叉堆 &amp; 堆排序

二叉堆,是一个满二叉树,满足堆的性质.即父节点大于等于子节点(max heap)或者是父节点小于等于子节点(min heap).二叉堆的如上性质常用于优先队列(priority queue)或是用于堆排序. 由于max heap 与min heap类似,下文只针对min heap进行讨论和实现. 如上图,是根据字母的ASCII码建立的最小堆. 我们用数组对满二叉树采用宽度优先遍历存储堆结构,如下图所示: 从数组下标1开始存储堆,这样的处理方式可以得到如下性质: 1.堆中的每个父节点k,他的两个子

二叉堆 及 大根堆的python实现

Python 二叉堆(binary heap) 二叉堆是一种特殊的堆,二叉堆是完全二叉树或者是近似完全二叉树.二叉堆满足堆特性:父节点的键值总是保持固定的序关系于任何一个子节点的键值,且每个节点的左子树和右子树都是一个二叉堆. 当父节点的键值总是大于或等于任何一个子节点的键值时为最大堆. 当父节点的键值总是小于或等于任何一个子节点的键值时为最小堆. 二叉堆的存储 二叉堆一般用数组来表示.如果根节点在数组中的位置是1,第n个位置的子节点分别在2n和 2n+1.因此,第1个位置的子节点在2和3,第2