堆的建立和操作

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

可以考虑利用一般的数组、链表,有序数组、链表或者是二叉搜索树、AVL树等进行实现但是都会出现各种各样的问题,以下是对复杂度的分析




如果用搜索二叉树,则很可能总是从一边删除,过若干次树就会变得很不均匀,最后我们想到了利用优先队列完全二叉树表示

堆具有两个特性

结构性:用数组表示的完全二叉树

有序性:任一结点的关键字是其子树所有结点的最大值(或最小值)

又分为

最大堆(MaxHeap),也称“大顶堆”:最大值

最小堆(MinHeap),也称“小顶堆”:最小值

下面举一下堆的例子


堆的抽象数据类型描述

类型名称:最大堆(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中最大元素(高优先级)
下面讲逐一进行实现

最大堆的插入

在最大堆的插入过程中,首先将元素插入最后一个位置,之后与其父节点进行对比,如果其父节点比其小,交换两个结点的值

其时间复杂性是O(logN),注意这里H->Elements[0]的用法,是哨兵元素,控制循环的进行
//最大堆的插入
void Insert(MaxHeap &H,ElementType item)
{//将元素item插入最大堆H,其中H->Elements[0]已经定义为哨兵
	int i;
	if(IsFull(H)){
		printf("最大堆已满\n");
		return;
	}
	i=++H->Size;//先Size自加1再赋给i,i指向插入后堆中的最后一个元素
	for(;H->Elements[i/2]<item;i/=2)
		H->Elements[i]=H->Elements[i/2];//向下过滤结点
	//哨兵的作用就是避免插入的值比Elements[0]还大
	H->Elements[i]=item;//将item插入,运行速度比交换快
	//时间复杂性是O(logN)
};
函数如上

最大堆的删除

删除时是先记录根节点的大小,之后把最后的一个元素作为根节点,然后与左右两个子结点比较,选择比较大的子结点与根节点进行交换,最后返回根节点,具体的实现函数如下
//从堆中删除一个元素
ElementType DeleteMax(MaxHeap H){//从最大堆H中取出键值最大的元素,并删除一个结点
	int Parent,Child;
	ElementType MaxItem,temp;
	if(IsEmpty(H)){
		printf("最大堆已为空\n");
		return ERROR;
	}
	MaxItem=H->Elements[1];//取出根节点(最大值)
	//用最大堆中的最后一个元素从根节点开始向上过滤下层结点
	temp=H->Elements[H->Size--];
	for(Parent=1;Parent*2<=H->Size;Parent=Child){
		Child=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->Elements[Child];
	}
	H->Elements[Parent]=temp;
	return MaxItem;
}


最大堆的建立

建立最大堆:将已经存在的N个元素按最大堆的要求存放在一个一维数组中
方法1:通过插入操作,将N个元素一个个相继插入到一个初始为空的堆中去,其时间代价最大为O(NlogN)
方法2:在线性时间复杂度下建立最大堆
(1)将N个玉努斯按输入顺序存入,先满足完全二叉树的结构特性
(2)调整各结点位置,以满足最大堆的有序特性
其线性时间复杂度由以下图片说明来证明


下面是函数的具体实现方法
/*----------- 建造最大堆 -----------*/
void PercDown( MaxHeap H, int p )
{ /* 下滤:将H中以H->Data[p]为根的子堆调整为最大堆 */
    int Parent, Child;
    ElementType X;
 
    X = H->Elements[p]; /* 取出根结点存放的值 */
    for( Parent=p; Parent*2<=H->Size; Parent=Child ) {
        Child = Parent * 2;
        if( (Child!=H->Size) && (H->Elements[Child]<H->Elements[Child+1]) )
            Child++;  /* Child指向左右子结点的较大者 */
        if( X >= H->Elements[Child] ) break; /* 找到了合适位置 */
        else  /* 下滤X */
            H->Elements[Parent] = H->Elements[Child];
    }
    H->Elements[Parent] = X;
}
 
void BuildHeap( MaxHeap H )
{ /* 调整H->Data[]中的元素,使满足最大堆的有序性  */
  /* 这里假设所有H->Size个元素已经存在H->Data[]中 */
 
    int i;
 
    /* 从最后一个结点的父节点开始,到根结点1 */
    for( i = H->Size/2; i>0; i-- )
        PercDown( H, i );
}
来看一下代码的执行结果
最后附上完整代码
#include <stdio.h>  
#include <stdlib.h>  

#define MaxData 1000
#define ERROR -1 /* 错误标识应根据具体情况定义为堆中不可能出现的元素值 */

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


//创建容量为MaxSize的空的最大堆
MaxHeap Create(int MaxSize);
//判断最大堆是否已满
int IsFull(MaxHeap H);
//最大堆的插入
void Insert(MaxHeap &H,ElementType item);
//判断最大堆是否为空
int IsEmpty(MaxHeap H);
//从堆中删除一个元素
ElementType DeleteMax(MaxHeap H);
//建造最大堆 
void PercDown( MaxHeap H, int p );
void BuildHeap( MaxHeap H );
//打印堆中元素
void Print(MaxHeap H);

int main(){

	MaxHeap H= Create(15);
	Insert(H,55);
	Insert(H,79);
	Insert(H,66);
	Insert(H,83);
	Insert(H,72);
	Insert(H,30);
	Insert(H,49);
	Insert(H,91);
	Insert(H,87);
	Insert(H,43);
	Insert(H,9);
	Insert(H,38);
	Print(H);
	DeleteMax(H);
	DeleteMax(H);
	Print(H);
	
	printf("\n");

	MaxHeap HH= Create(15);
	int a[]={55,79,66,83,72,30,49,91,87,43,9,38};
	int len=sizeof(a)/sizeof(int);
	HH->Size=len;
	for(int i=0;i<len;i++)
		HH->Elements[i+1]=a[i];
	Print(HH);
    BuildHeap(HH);
	Print(HH);

	system("pause");
}

//创建容量为MaxSize的空的最大堆
MaxHeap Create(int MaxSize){
	MaxHeap H=(MaxHeap)malloc(sizeof(struct HeapStruct));
	H->Elements=(ElementType *)malloc((MaxSize+1)*sizeof(ElementType));//从小标为1的地方开始存放
	H->Size=0;
	H->Capacity=MaxSize;
	H->Elements[0]=MaxData;//定义“哨兵”为大于堆中所有可能元素的值,便于以后更快操作

	return H;
	//时间复杂性是O(logN)
};

//判断最大堆是否已满
int IsFull(MaxHeap H){
	return(H->Size==H->Capacity);
};


//最大堆的插入
void Insert(MaxHeap &H,ElementType item)
{//将元素item插入最大堆H,其中H->Elements[0]已经定义为哨兵
	int i;
	if(IsFull(H)){
		printf("最大堆已满\n");
		return;
	}
	i=++H->Size;//先Size自加1再赋给i,i指向插入后堆中的最后一个元素
	for(;H->Elements[i/2]<item;i/=2)
		H->Elements[i]=H->Elements[i/2];//向下过滤结点
	//哨兵的作用就是避免插入的值比Elements[0]还大
	H->Elements[i]=item;//将item插入,运行速度比交换快
	//时间复杂性是O(logN)
};


//判断最大堆是否为空
int IsEmpty(MaxHeap H){
	return(H->Size==0); 
};

#define ERROR -1 /* 错误标识应根据具体情况定义为堆中不可能出现的元素值 */

//从堆中删除一个元素
ElementType DeleteMax(MaxHeap H){//从最大堆H中取出键值最大的元素,并删除一个结点
	int Parent,Child;
	ElementType MaxItem,temp;
	if(IsEmpty(H)){
		printf("最大堆已为空\n");
		return ERROR;
	}
	MaxItem=H->Elements[1];//取出根节点(最大值)
	//用最大堆中的最后一个元素从根节点开始向上过滤下层结点
	temp=H->Elements[H->Size--];
	for(Parent=1;Parent*2<=H->Size;Parent=Child){
		Child=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->Elements[Child];
	}
	H->Elements[Parent]=temp;
	return MaxItem;
}


/*----------- 建造最大堆 -----------*/
void PercDown( MaxHeap H, int p )
{ /* 下滤:将H中以H->Data[p]为根的子堆调整为最大堆 */
    int Parent, Child;
    ElementType X;
 
    X = H->Elements[p]; /* 取出根结点存放的值 */
    for( Parent=p; Parent*2<=H->Size; Parent=Child ) {
        Child = Parent * 2;
        if( (Child!=H->Size) && (H->Elements[Child]<H->Elements[Child+1]) )
            Child++;  /* Child指向左右子结点的较大者 */
        if( X >= H->Elements[Child] ) break; /* 找到了合适位置 */
        else  /* 下滤X */
            H->Elements[Parent] = H->Elements[Child];
    }
    H->Elements[Parent] = X;
}
 
void BuildHeap( MaxHeap H )
{ /* 调整H->Data[]中的元素,使满足最大堆的有序性  */
  /* 这里假设所有H->Size个元素已经存在H->Data[]中 */
 
    int i;
 
    /* 从最后一个结点的父节点开始,到根结点1 */
    for( i = H->Size/2; i>0; i-- )
        PercDown( H, i );
}

//打印堆中元素
void Print(MaxHeap H){
	printf("H中的元素为:\n");
	for(int i=1;i<=H->Size;i++)
		printf("%d ",H->Elements[i]);
	printf("\n");
}
done,继续做下去√

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

拉风小宇

请我喝个咖啡呗

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值