一、堆的定义:
一个(二叉)堆是一个几乎完全二叉树,它的每个节点都有满足堆的每个特性:如果v和p(v)分别是它的节点和父节点,那么存储在p(v)中的数据项中的键值不小于(或不大于)存储在v中的数据项的键值。
堆的数据结构支持以下运算:
Delete-max[h]:从一个非空的推H中删除最大键值的数据项并将数据项返回。
Insert[h,x]:插入x到堆中。
Delete[h,i]:从堆中删除第i项
Makehaep[A]:将数组A转换成堆
这个堆的特性蕴含着:沿着每条从根到叶子的路径,元素的健值以非升序排列:
a.T的根节点存储在H[1]中。
b.假设T的节点存储在H[j]中,如果它有左子节点,这个子节点存储在H[2j]中,如果它也有右节点,这个子节点存储在H[2j+1]中。
c.元素H[j]的父节点如果不是根节点,则存储在H[j/2]中。
堆可以看以一棵完全二叉树,它实际上是一个数组H[1..N]。它有如下性质:对于任何索引j,2<=j<=N,
,如下图:
二、堆的运算。
堆上运算(上移、下移、插入、删除、最除最大值)、创建堆、堆排序。下面通过一个具体的实例来演示这个操作。
2.1堆上操作
假定对于某个i>1,H[i]变成了健值大于它父结点的健值的元素,这样就违返的堆的特性,因此这样的数据结构就不成为堆了,要修复堆的特性,需要用Sift-up把新数据项移动合适的位置。即:运算沿着从H[i]到根结点的唯一一条路径,把H[i]移动到合适的位置。在沿着路径的每一步上,都将H[i]与其父节点我建值相比较。
算法如下:
procedure siftup(var h:arr;i:integer);//上移操作,当某个节点值大于其父节点时
var
done:boolean;
t:integer;
begin
if i=1 thenexit;//如果要移动的节点是第一个节点,则退出
repeat
done:=false;
ifh[i]>h[i div 2]//同它的父节点比较,如果大于父节点
thenbegin//则交换
t:=h[i];
h[i]:=h[idiv 2];
h[i div2]:=t;
end
elsedone:=true;//否则已成为一个新的堆
i:=i div2;//回到父节点
until (i=1)or done;//直到回的根节点或完成堆操作
end;
2.2堆下运算
假定对于,存储在H[i]的元素的值小于H[2i]和H[2i+1](如果存在的话)中最大的一个,这样就违返的堆的特性,树就不再成为一个堆了。如果要修复堆,要使用Sift_down操作运算把H[i]“渗”到合适的位置上,沿着这条路径每一步,都把H[i]的健值和存储在它的子节点(如果存在)两个健值最大那个相比较。
procedure siftdown(varh:arr;n,i:integer);//下移操作,当某个结点的值小于子结点
var
done:boolean;
t:integer;
begin
done:=false;
if 2*i>nthen exit;//当节点i没有子节点时,则退出
repeat
i:=2*i;//左子节点序号
if(i+1<=n) and (h[i+1]>h[i]) then i:=i+1;//找出健值更大的子节点
if h[i div2]<h[i]//父结点同较大子节点比较,如果父节点小于较大子节点
thenbegin//则交换
t:=h[i];
h[i]:=h[idiv 2];
h[i div2]:=t;
end
elsedone:=true;//否则堆建好
until(2*i>n) or done;{直到建好堆或者无子节点}
end;
2.3堆的插入
为了把元素x插入堆H中,先将堆的大小加1,然后将x添加到堆的末尾,再根据需要,把x上移,直到满足堆的特性。
procedure heapinsert(var h:arr;varn:integer;x:integer);//插入新元素
begin
inc(n);//长度增一
h[n]:=x;//放在最后
siftup(h,n);//上移操作
end;
2.4删除元素
要从大小为n的堆删除元素H[i],可先用H[i]替换H[i],然后将堆大小减1,如果需要的话,将H[i]的值与存储在它父节点和子节点中元素健值的关系,对H[i]做出上移或下移的操作,直到满足堆的特性。
procedure heapdelete(var h:arr;varn:integer;i:integer);//删除某一节点
var
x,y:integer;
begin
x:=h[i];//取节点值存入x中
y:=h[n];//取最后一个节点值存入y中
dec(n);
if i=n+1then exit;//如果是最后一个节点则退出
h[i]:=y;//把最后一个节点存入第i个节点中
if y>=xthen siftup(h,i)//如果比原来的节点大则上移
elsesiftdown(h,n,i);//否则下移
end;
2.5删除最大值
这项运算是在一个非空堆H中删除并返回最大健值的数据项。在堆中返回最大健值的元素需要O(1)的时间,因为这个元素是树的根节点。然而由于删除根节点破坏了这个堆,必须要修复这个堆。修复时同删除堆中任意一节点一样,由于删除点为堆的根节点,所以用下移操作。
procedure heapdeletemax(var h:arr;varn,x:integer);//删除根节点
begin
x:=h[1];
heapdelete(h,n,1);
end;
2.6创建堆
给出一个有n个元素的数组A[1..n],要创建一个包含这些元素的堆是容易的,可以这样进行:从空堆开始,不断插入每一个元素,直到A完全被子转移成堆为止。因为插入第j个元素用进O(lgj),因此这种方法创健堆的时间复杂度为O(nlgn)。
有趣的是,可以证明能在O(n)的时间内创建堆,用n个元素来创建堆,实现细节如下,我们知道对应于堆H[1..n]的树节点可以方便地以自顶向下,从左到右的方式从1到n编号。这样编号后,可以用以下的方法,把一棵n个节点的几乎完全二叉树转换成堆H[1..n],从最后一个节点开始(编号为n那个)到根节点(编号为1那个),逐个扫描所有的节点,要据需要,每次将以前的节点为根节点的子树转换成堆。
首先把数组A[1..n]=(4,3,8,10,11,13,7,30,17,26)转换成堆。图形演示如下:
程序代码:
procedure makeheap(var h:arr;n:integer);//建堆
var
i:integer;
begin
for i:=n div2 downto 1 do//从非叶子节点开始下移操作
siftdown(h,n,i);
end;
这里就放一个排序的小头堆就好了
const
maxn=200000;
var
a,f:array [0..maxn] of longint;
i,j,n,t:longint;
procedure siftup(i:longint);
var
c,j,n:longint;
begin
if f[i]<f[i div 2] then
begin
c:=f[i];
f[i]:=f[i div 2];
f[i div 2]:=c;
siftup(i div 2);
end;
end;
procedure siftdown(x:longint);
var
c,p,q:longint;
begin
if (f[x]>f[x*2+1]) and (f[x*2+1]<f[x*2]) and (x<=t) then
begin
c:=f[x];
f[x]:=f[x*2+1];
f[x*2+1]:=c;
siftdown(x*2+1);
end else
if (f[x]>f[x*2]) and (f[x*2]<=f[x*2+1]) and (x<=t) then
begin
c:=f[x];
f[x]:=f[x*2];
f[x*2]:=c;
siftdown(x*2);
end;
end;
begin
fillchar(f,sizeof(f),$7f);
f[0]:=0;
readln(n);
for i:=1 to n do
read(a[i]);
t:=n;
for i:=1 to n do
begin
f[i]:=a[i];
siftup(i);
end;
while t>1 do
begin
write(f[1],' ');
f[1]:=f[t];
f[t]:=maxlongint;
dec(t);
siftdown(1);
end;
writeln(f[1]);
end.
本文详细介绍了堆的定义,包括其性质和在数组中的表示。堆的常见操作如上移、下移、插入、删除以及创建堆等进行了阐述,并提供了相应的Pascal代码实现。堆在数据结构中扮演着重要角色,常用于优先队列和排序算法如堆排序。

647

被折叠的 条评论
为什么被折叠?



