您的位置:首页 > 其它

堆及其操作

2016-04-22 18:08 141 查看
1. 背景:

队列,他的一个基本特征是“先进先出”或者说是“先来先服务”,队列中的任何原始都没特权,前面的元素未被处理完,后面的元素只能等待。

但在现实世界中,许多情况需要有特权。例如:有许多客户在一个打印店排队等待打印资料,某一个客户需要打印数百张的一本书,而排在他后面的客户只需打印两页的一份简历。我们不妨把前面成为大作业,而后面称为小作业。这时小作业客户对店主和大作业客户提出是否可以先为他打印只有两页的简历,也就是说是否允许他“插队”。遇到这种情形,店主和大作业客户往往能接受小作业客户的请求。此时,违背了队列先来先服务的策略,是一种“小作业”优先的排队策略。

优先队列(Priority Queue):特殊的“队列”,取出元素的顺序是依照元素的优先权(关键字)大小,而不是元素进入队列的先后顺序。

2. 采用数据或链表实现优先队列:

1) 数组 :
插入 — 元素总是插入尾部 ~  ( 1 )
删除 — 查找最大(或最小)关键字 ~  ( n )
     从数组中删去需要移动元素 ~ O( n )
2) 链表:
插入 — 元素总是插入链表的头部 ~  ( 1 )
删除 — 查找最大(或最小)关键字 ~  ( n )
             删去结点 ~ ( 1 )
3) 有序数组:
插入 — 找到合适的位置 ~ O( n ) 或 O(log2 n )
             移动元素并插入 ~ O( n )
删除 — 删去最后一个元素 ~ ( 1 )
4) 有序链表:
插入 — 找到合适的位置 ~ O( n )
插入元素 ~ ( 1 )
删除 — 删除首元素或最后元素 ~ ( 1 )

3. 优先队列的完全二叉树表示:





堆的两个特性:
结构性:用数组表示的完全二叉树;
有序性:任一结点的关键字是其子树所有结点的最大值(或最小值)
 “最大堆(MaxHeap)”,也称“大顶堆”:最大值
 “最小堆(MinHeap)”,也称“小顶堆” :最小值


4. 案例:






5. 堆的抽象数据类型描述

类型名称:最大堆(MaxHeap) 

数据对象集:完全二叉树,每个结点的元素值不小于其子结点的元素值 操作集:最大堆H  MaxHeap,元素item  ElementType,主要操作有:

•MaxHeap Create( int MaxSize ):创建一个空的最大堆。

•Boolean IsFull( MaxHeap H ):判断最大堆H是否已满。

•Insert( MaxHeap H, ElementType item ):将元素item插入最大堆H。

•Boolean IsEmpty( MaxHeap H ):判断最大堆H是否为空。

•ElementType DeleteMax( MaxHeap H ):返回H中最大元素(高优先级)。


6. 最大堆的创建:

typedef struct HeapStruct* MaxHeap;
struct HeapStruct
{
ElementType* Elements;/*存储堆元素的数组*/
int Size;/*堆的当前元素个数*/
int Capacity;/*堆的最大容量*/
};


注意:

当根据用户输入的MaxSize建立空的最大堆时,数组应该有MaxSize + 1个元素,因为数组起始单元为1,元素是存在第1 ~ MaxSize个单元里的。通常第0个单元是无用,但是如果事先知道堆中所有元素的取值范围,也可以为第0单元赋一个特殊的值MaxData,这个值比堆中任何一个可能的元素都要大。

MaxHeap Create(int MaxSize)
{
MaxHeap H = malloc(sizeof(struct HeapStruct));
H->Elements = malloc((MaxSize + 1) * sizeof(ElementType));
H->Size = 0;
H->Capacity = MaxSize;
H->Elements[0] = MaxData; //吧MaxData换成小于堆中所有元素的MinData,同样适用于创建最小堆。
/*定义“哨兵”为大于堆中所有可能元素的值,便于更快操作*/
return H;
}
7. 插入:将新增结点插入到从其父结点到根结点的有序序列中

H->Element[0]是哨兵元素,它不小于堆中的最大元素,控制顺序环结束。
T(N) = O(logN)

void Insert(MaxHeap H, ElementType item)
{
/*将元素item插入最大堆H,其中H->Elements[0]已经定义为哨兵*/
int i;
if(IsFull(H))
{
printf("最大堆已满");
return;
}
i = ++H->Size;/*i指向插入后堆中的最后一个元素的位置*/
for(;H->Elements[i/2] < item; i/=2)
{
H->Elements[i] = H->Elements[i/2];/*向下过过滤结点*/
}
H->Elements[i] = item;
}
8.堆的删除:

T(N) = O(logN)



1) 左儿子:2i
2) 右儿子:2i + 1
3) Child != H->Size 判断这个结点是否有右儿子

ElementType DeleteMax(MaxHeap H)
{/*从最大堆中H中取出键值为最大的元素,并删除一个结点*/
int Parent, Child;
ElementType MaxItem, temp;
if(IsEmpty(H))
{
printf("最大堆已为空");
return;
}
MaxItem = H->Elements[1];/*取出根结点最大值*/
/*用最大堆中最后一个元素从根节点开始向上过滤下层结点*/
temp = H-Elements[H->Size--];
for(Parent = 1; Parent * 2<= H->size; Parent = Child)
{
Chil = Parent * 2;
if((Child != H->Size) && (H->Elements[Child] < H->Elements[Child + 1]))
{
Child++;/*child指向左右子结点的较大者*/
}
if(temp >= H->Elements[Child])
{
break;
}
else
{/*移动temp元素到下一层*/
H->Elements[Parent] = H->Elemens[Child];
}
}
H-Elements[Parent] = temp;
}


9. 最大堆的建立:
堆的一个应用:堆排序
--需要先建堆
建立最大堆:将已经存在的N个元素按最大堆的要求存放在一个一维数组中。
方法1:通过插入操作,将N个元素一个个相继插入到一个初始为空的堆中去,其时间代价最大为O(NlogN)

方法2:在线性时间复杂度下建立最大堆。

(1) 在N个元素按输入顺序存入,先满足完全二叉树的结构特性。

(2) 调整各结点位置,以满足最大堆的有序特性。



10. 最大堆建立代码:

MAxHeap BuildMaxHeap(MAxHeap H)
{
/*这里假设所有H->Size个元素已经存在H->Elements[]中*/
/*本函数将H->Elements[]中的元素调整,使满足最大堆的有序性*/
int Parent, Child, i;
ElementType temp;
for(i = H->Size/2;i > 0; i--)
{/*从最后一个结点的父结点开始*/
temp = H->Elements[i];
for(Parent = i;Parent * 2 <= H->Size;Parent = Child)
{
/*向下过滤*/
Child = Parent * 2;
if((Child != H->Size) && H->Elements[Child] < H->Elements[Child + 1])
{
Child++;
}
if(temp >= H->Elements[Child])
{
break;
}
else
{/*移动temp元素到下一层*/
H->Elements[Parent] = H->Elements[Child];
}
}/*结束内部for循环堆以H->Elements[i]为根的子树的调整*/
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  优选队列 算法