堆的基本操作的实现

什么是堆?
是一种特殊的“队列”,取出元素的顺序依照元素的优先权(关键字)大小,而不是元素进入队列的先后顺序。(可以用STL库中的优先队列代替,下面也是其实现原理,虽然可以直接用优先队列,但原理也很重要)
堆的特性:
(1)结构特性:用数组表示的完全二叉树;
(2)有序性:任一结点的关键字是其子树所有结点的最大值或最小值(对应最大堆、最小堆)。
故对堆进行操作时一定不能破坏这两特性。

堆的基本操作:

#define MaxData 。。。//建议设一个堆的最大元素值作为哨兵,在对堆进行操作时你会发现很方便。当然只是建议。
typedef struct node *Heap;
struct node{
	ElementType *Elements;//储存堆元素的指针
	int size;//当前堆元素个数
	int Capacity;//	堆容量
};

(1)堆的建立:

Heap CreateMaxHeap(int maxsize)
{
	Heap H=new node;
	H->Elements=new ElementType[maxsize+1];
	H->size=0;
	H->Capacity=maxsize;
	H->Elements[0]=MaxData;//设一个堆的最大元素值放在0位置上
	return H;
}

下面的操作均已最大堆为例,其实最小堆就是小的节点优先替换父节点,看了下面的自己模仿实现。

(2)判空判满:

bool IsFull( MaxHeap H )
{
    return (H->Size == H->Capacity);
}
bool IsEmpty( MaxHeap H )
{
    return (H->Size == 0);
}

在下面的函数中,因为**有了哨兵H->Elements[0],不用加i>1的循环条件,左子节点就是父节点的序号的2倍!!!**不过最重要的是自己画图写代码体会!!!

(3)最大堆的插入:

前面已经说了,对堆进行操作时一定不能破坏其特性。所以我们在插入时先将要插入的元素作为最后一个叶子结点,先满足结构特性,在进行调整满足有序性。
在这里插入图片描述

bool Insert( Heap H, ElementType X )
{ /* 将元素X插入最大堆H,其中H->Data[0]已经定义为哨兵 */
    int i;
  
    if ( IsFull(H) ) { 
        printf("最大堆已满");
        return false;
    }
    i = ++H->Size; /* i指向插入后堆中的最后一个元素的位置 */
    for ( ; H->Data[i/2] < X; i/=2 )
        H->Data[i] = H->Data[i/2]; /* 上滤X */
    H->Data[i] = X; /* 将X插入 */
    return true;
}

(4)删除最大堆顶元素(根节点):

前面说到堆是一种特殊的“队列”,取出元素的顺序依照元素的优先权(关键字)大小,所以删除操作其实是删除根节点。我们的做法是让最后一个叶子结点代替根节点,先满足结构特性,在进行调整满足有序性。
在这里插入图片描述

#define ERROR -1 /* 错误标识应根据具体情况定义为堆中不可能出现的元素值 */
ElementType PercDown(Heap H)//返回根节点
//把根节点为H->Elements[p]的子堆调整为最大堆。
{
	if ( IsEmpty(H) ) {
        printf("最大堆已为空");
        return ERROR;
    }
	int child,parent;
	ElementType MAX=H->Elements[1];//根节点
	ElementType tmp=H->Elements[H->size--];//--是因为删除了根节点。
	for(parent=p;parent*2<=H->size;parent=child){
		child=parent*2;//得到左子节点序号
		if( child!=H->size && H->Elements[child] < H->Elements[child+1] )
			//取左右子节点中大的那个(毕竟要让最大的在上面)
			child+=1;
		if(tmp>=H->Elements[child]) break;
		else	//如果子节点大于父节点就要让子节点代替父节点
			H->Elements[parent]=H->Elements[child];
	}
	H->Elements[parent]=tmp;
	return MAX;
}

建立最大堆:

显然根据树中的经验,对于一个含有N个元素的序列,我们有两种方式建立最大堆,第一种是一个个用Insert函数插入,第二种是先按照序列中的顺序建立一个完全二叉树满足结构特性,再调整各节点位置来满足有序性。
第一种方法很飘,因为我们不知道这个序列的顺序无法控制时间复杂度,如果这个序列是按最小堆排序的话(最坏情况)我们的时间复杂度将是O(NlogN);
第二种方法我们把两个特性分开实现,可以在线性时间复杂度实现建立最大堆,所以我们用第二种方法。调整的操作和删除类似。

int N,data[N];//这样定义全局变量不对,只是用于代表序列
Heap CreateMaxHeap(//参数根据需要写)//要对上面的建堆函数做一些小修改,其实本就应灵活变通
{
	Heap H=new node;
	H->Elements=new ElementType[N+1];
	for(int i=1;i<=N;i++){
		H->Elements[i]=data[i-1];//data是从0到n-1。
	}
	H->size=0;
	H->Capacity=N;
	H->Elements[0]=MaxData;//设一个堆的最大元素值放在0位置上
	return H;
}
void PercDown(Heap H,int p)//下滤
//把根节点为H->Elements[p]的子堆调整为最大堆。
{
	int child,parent;
	ElementType tmp=H->Elements[p];//用tmp把子堆的根节点存起来
	for(parent=p;parent*2<=H->size;parent=child){
		child=parent*2;//得到左子节点序号
		if( child!=H->size && H->Elements[child] < H->Elements[child+1] )
			//取左右子节点中大的那个(毕竟要让最大的在上面)
			child+=1;
		if(tmp>=H->Elements[child]) break;
		else	//如果子节点大于父节点就要让子节点代替父节点
			H->Elements[parent]=H->Elements[child];
	}
	H->Elements[parent]=tmp;
}
void BuildMaxHeap(Heap H)
{//从最后一个父节点开始下滤,方便的一批
	for(int i=H->size/2;i>=1;i--)
		PercDown(H,i);
}

代码中有很多小细节,还需要慢慢体会其中的一些用意。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值