二叉堆简介

平时所说的堆,若没加任何修饰,一般就是指二叉堆。同二叉树一样,堆也有两个性质,即结构性和堆序性。正如AVL树一样,对堆的以此操作可能破坏者两个性质中的一个,因此,堆的操作必须要到堆的所有性质都被满足时才能终止。

结构性质

堆是一棵完全填满的二叉树,因为完全二叉树很有规律,所以它可以用一个数组表示而不需要指针。如下图所示,图2中的数组对应图1中的堆。

                 

图1:二叉堆                                                                                   图2:二叉堆的数组存储

对于任意位置i上的元素,其左儿子在位置2i处,右儿子在位置2i+1处,而它的父亲在i/2。因此,不仅指针这里不需要,而且遍历该树所需要的操作也十分简单。这种表示法的唯一问题在于:最大的堆大小需要事先估计,但对于典型的情况者并不成问题,图2中堆的大小是13个元素。该数组有一个位置0,用做哨兵,后面会有阐述。

因此,一个堆的数据结构将由一个数组,一个代表最大值的整数以及当前堆的大小组成。

堆序性质

使操作能快速执行的性质是堆序性。在一个堆中,对于每个节点X,X的父亲中的关键字小于(或等于)X中的关键字,根节点除外(根节点没有父亲)。图3中,左边的是堆,右边的不是(虚线表示堆序性质被破坏)。

图3:两棵完全二叉树

基本操作

Insert(插入):

为了将一个元素X插入到堆中,我们在下一个空闲位置创建一个空穴,否则该堆将不是完全树。如果X可以放入到该空穴中,那么插入完成。否则,我们把空穴的父节点上的元素移入该空穴中,这样,空穴就朝着根的方向上行一步。继续该过程直到X能被放入到空穴中为止。图4表示,为了插入14,我们在堆的下一个可用位置建立一个空穴,由于将14插入空穴破坏了堆序性质,因此将31移入该空穴,图5继续这种策略,直到找到14的正确位置。

               

图4:创建一个空穴,再将空穴上冒                                       图5:将14插入到前面的堆中的其余两步

这种策略叫做上虑。新元素在堆中上虑直到找出正确的位置;使用如下代码,很容易实现。

如果要插入的元素师新的最小值,那么它将一直被推向顶端,这样在某一时刻,i将是1,我们就需要令程序跳出while循环。当然可以通过明确的测试做到这一点。不过,这里采用的是把一个很小的值放到位置0处以使while循环终止,这个值必须小于堆中的任何值,称之为标记或哨兵。这类似于链表中头结点的使用。通过添加的这个标记,避免了每次循环都要执行一次测试,这是简单的空间换时间策略。

DeleteMin(删除最小元):

找出最小元是很容易的;困难的部分是删除它。当删除一个最小元时,在根节点处产生了一个空穴。由于现在堆少了一个元素,因此对中最后一个元素X必须移到该堆的某个地方。如果X可以被放入空穴中,那么DeleteMin完成。不过这一般都不可能,因此我们将空穴的两个儿子中较小者移入空穴中,这样就把空穴向下推了一层,重复该步骤,知道X可以被放入空穴中。因此,我们的做法是将X置入沿着从根开始包含最小儿子的一条路径上的一个正确的位置。

图6显示DeleteMin之前的堆,删除13之后,我们必须要正确的将31放到堆中,31不能放在空穴中,因为这将破坏堆序性质,于是,我们把较小的儿子14置入空穴,同时空穴向下滑一层,重复该过程,把19置入空穴,在更下一层上建立一个新的空穴,然后26置入空穴,在底层又建立一个新的空穴,最后,我们得以将31置入空穴中。这种策略叫做下虑。

          

图6 在根处建立空穴                                                             图7:将空穴下滑一层

 图8:空穴移到底层,插入31
BinHeap.h
typedef int ElementType;
#ifndef _BinHeap_H
#define MinPQSize 10
#define MinData -32767
struct HeapStruct;
typedef struct HeapStruct *PriorityQueue; PriorityQueue Initialize(int MaxElements);
void Destroy(PriorityQueue H);
void MakeEmpty(PriorityQueue H);
void Insert(ElementType X,PriorityQueue H);
ElementType DleteMin(PriorityQueue H);
ElementType FindMin(PriorityQueue H);
int IsEmpty(PriorityQueue H);
int IsFull(PriorityQueue H); #endif

BinHeap.c

#include"BinHeap.h"
#include"fatal.h" struct HeapStruct
{
int Capacity;
int Size;
ElementType *Elements;
}; PriorityQueue Initialize(int MaxElements)
{
PriorityQueue H;
if(MaxElements<MinPQSize)
Error("Priority queue size is too small");
H=malloc(sizeof(struct HeapStruct));
H->Elements=malloc((MaxElements+)*sizeof(ElementType));
if(H->Elements==NULL)
FatalError("Out of space!!!");
H->Capacity=MaxElements;
H->Size=;
H->Elements[]=MinData;
return H;
} void MakeEmpty(PriorityQueue H)
{
H->Size=;
} void Insert(ElementType X,PriorityQueue H)
{
int i;
if(IsFull(H))
{
Error("Priority queue is full");
}
for(i=++H->Size;X<H->Elements[i/];i=i/)
{
H->Elements[i]=H->Elements[i/];
}
H->Elements[i]=X;
} ElementType DeleteMin(PriorityQueue H)
{
int i,Child;
ElementType MinElement,LastElement;
if(IsEmpty(H))
{
Error("Priority queue is empty");
return H->Elements[];
}
MinElement=H->Elements[];
LastElement=H->Elements[H->Size--];
for(i=;i<H->Size;i=Child)
{
Child=*i;
if(Child!=H->Size&&H->Elements[Child]>H->Elements[Child+])
Child++;
if(LastElement>H->Elements[Child])
H->Elements[i]=H->Elements[Child];
else
break;
}
H->Elements[i]=LastElement;
return MinElement;
} ElementType FindMin(PriorityQueue H)
{
return H->Elements[];
} int IsEmpty(PriorityQueue H)
{
return H->Size==;
} int IsFull(PriorityQueue H)
{
return H->Capacity==H->Size;
}
void Destroy(PriorityQueue H)
{
free(H->Elements);
free(H);
}

UseBinHeap.c

#include <stdio.h>
#include <stdlib.h>
#include"BinHeap.h"
int main()
{
int i;
PriorityQueue H=Initialize(MinPQSize);
MakeEmpty(H);
for(i=;i<MinPQSize;i++)
{
Insert(i,H);
}
printf("Hello world!\n");
return ;
}

d-堆

二叉堆因为实现简单,因此在需要优先队列的时候几乎总是使用二叉堆。d-堆是二叉堆的简单推广,它恰像一个二叉堆,只是所有的节点都有d个儿子(因此,二叉堆又叫2-堆)。下图表示的是一个3-堆。注意,d-堆要比二叉堆浅得多,它将Insert操作的运行时间改进为 。然而,对于大的d,DeleteMin操作费时得多,因为虽然树浅了,但是d个儿子中的最小者是必须找到的,如果使用标准算法,将使用d-1次比较,于是将此操作的时间提高到 。如果d是常数,那么当然两种操作的运行时间都为 O(logN)。虽然仍可以使用一个数组,但是,现在找出儿子和父亲的乘法和除法都有个因子d,除非d是2的幂,否则会大大增加运行时间,因为我们不能再通过二进制移位来实现除法和乘法了。D-堆在理论上很有趣,因为存在许多算法,其插入次数比删除次数多得多,而且,当优先队列太大不能完全装入内存的时候,d-堆也是很有用的,在这种情况下,d-堆能够以与B-树大致相同的方式发挥作用。

除了不能执行Find操作外(指以对数执行),堆的实现最明显的两个缺点是:将两个堆合并成一个堆是很困难的。这种附加的操作叫做Merge。存在许多实现堆的方法使得Merge操作的运行时间为O(logN),如下篇介绍的左式堆。

优先队列之二叉堆与d-堆的更多相关文章

  1. 图论——Dijkstra+prim算法涉及到的优先队列(二叉堆)

    [0]README 0.1)为什么有这篇文章?因为 Dijkstra算法的优先队列实现 涉及到了一种新的数据结构,即优先队列(二叉堆)的操作需要更改以适应这种新的数据结构,我们暂且吧它定义为Dista ...

  2. 《Algorithms算法》笔记:优先队列(2)——二叉堆

    二叉堆 1 二叉堆的定义 堆是一个完全二叉树结构(除了最底下一层,其他层全是完全平衡的),如果每个结点都大于它的两个孩子,那么这个堆是有序的. 二叉堆是一组能够用堆有序的完全二叉树排序的元素,并在数组 ...

  3. 优先队列的二叉堆Java实现

    package practice; import edu.princeton.cs.algs4.StdRandom; public class TestMain { public static voi ...

  4. PriorityBlockingQueue优先队列的二叉堆实现

    转载请注明原创地址http://www.cnblogs.com/dongxiao-yang/p/6293807.html java.util.concurrent.PriorityBlockingQu ...

  5. PAT树_层序遍历叶节点、中序建树后序输出、AVL树的根、二叉树路径存在性判定、奇妙的完全二叉搜索树、最小堆路径、文件路由

    03-树1. List Leaves (25) Given a tree, you are supposed to list all the leaves in the order of top do ...

  6. 数据结构与算法——优先队列类的C++实现(二叉堆)

    优先队列简单介绍: 操作系统表明上看着是支持多个应用程序同一时候执行.其实是每一个时刻仅仅能有一个进程执行,操作系统会调度不同的进程去执行. 每一个进程都仅仅能执行一个固定的时间,当超过了该时间.操作 ...

  7. 纯数据结构Java实现(6/11)(二叉堆&优先队列)

    堆其实也是树结构(或者说基于树结构),一般可以用堆实现优先队列. 二叉堆 堆可以用于实现其他高层数据结构,比如优先队列 而要实现一个堆,可以借助二叉树,其实现称为: 二叉堆 (使用二叉树表示的堆). ...

  8. Python实现二叉堆

    Python实现二叉堆 二叉堆是一种特殊的堆,二叉堆是完全二元树(二叉树)或者是近似完全二元树(二叉树).二叉堆有两种:最大堆和最小堆.最大堆:父结点的键值总是大于或等于任何一个子节点的键值:最小堆: ...

  9. 二叉堆的BuildHeap操作

    优先队列(二叉堆)BuildHeap操作 \(BuildHeap(H)\)操作把\(N\)个关键字作为输入并把它们放入空堆中.显然,这可以使用\(N\)个相继的\(Insert\)操作来完成.由于每个 ...

随机推荐

  1. js实现栈结构

    实现栈结构 //创建栈 function Stack (){ let items = [] this.push = function(element){ items.push(element) } t ...

  2. 浅谈ul布局以及table布局

    我个人对于某些言论说要注重html语义化在布局中的应用,我反而不怎么感冒,试试兼容IE7&&项目期相对较赶的情况下,我还是推荐快速开发为主,兼容性强为主. 如果布局中需要用户边框,推荐 ...

  3. BZOJ4589: Hard Nim(FWT 快速幂)

    题意 题目链接 Sol 神仙题Orzzzz 题目可以转化为从\(\leqslant M\)的质数中选出\(N\)个\(xor\)和为\(0\)的方案数 这样就好做多了 设\(f(x) = [x \te ...

  4. js 判断 复选框全选、全不选、反选、必选一个

    一个挺 使用的 js 代码片段,  判断  复选框全选.全不选.反选.必选一个 记录下, 搬来的 思路: 修改数据的 选中与否状态, 拿到所有的输入框,看是否有选中的状态 <html> & ...

  5. IDEA Properties中文unicode转码问题

    在IDEA中创建了properties文件,发现默认中文不会自动进行unicode转码.如下 在project settings - File Encoding,在标红的选项上打上勾,确定即可 效果图 ...

  6. 【Oracle】锁表处理 SQL 错误: ORA-00054: 资源正忙, 但指定以 NOWAIT 方式获取资源, 或者超时失效

    问题描述有时候ORACLE数据的某些表由于频繁操作,而且比较大,会导致锁表(死锁). 问题分析(1)锁的分析ORACLE里锁有以下几种模式:0:none1:null 空2:Row-S 行共享(RS): ...

  7. 一些baidu面经

    百度问的一些问题供参考: 1. epoll 和 select,epoll 两种模式,阻塞非阻塞: 2. 两个严格递增链表找出相同的元素组成新的链表: ref1   ref 3. 网络传输中如何传送一个 ...

  8. 为exchange 2010 owa 添加验证码

    微软给了exchange owa页面加固的方案,如有需要,请查看. https://partnersupport.microsoft.com/zh-hans/par_servplat/forum/pa ...

  9. December 07th 2016 Week 50th Wednesday

    Life is a flower, and love is the honey of the flower. 人生是花儿,而爱情就是花的蜜. My life is not as beautiful a ...

  10. 使用python 操作liunx的svn,方案一

    在服务器中要做几个操作,使用命令操作svn,svn文件的创建,svn文件更新,并把指定demo路径,移动到创建的文件夹中,进行提交, # -*- coding:utf-8 -*- import pys ...