数据结构-树-堆(heap)-04

堆(heap)

什么是堆

优先队列(Priority Queue):特殊的“队列”,取出元素的顺序是 依照元素的优先权(关键字)大小,而不是元素进入队列的先后顺序。我们可以利用完全二叉树来表示优先队列,也就是堆

堆的特性

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

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

    • “最大堆(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中最大元素(高优先级)。

最大堆的操作

最大堆创建

  • 最大堆是由数组表示的
  • H->Elements = malloc( (MaxSize+1) * sizeof(ElementType))中,由于是一段数组,我们要给他赋予一块内存空间,同时Maxsize+1的原因是堆在第0项不放东西,我们一般存放一个哨兵节点,在最大堆中,哨兵节点存放一个比所有值都大的数字,这样可以免去在插入过程中判断i是否 = 1
//最大堆的创建
typedef struct HeapStruct *MaxHeap;
struct HeapStruct {
	ElementType *Elements; /* 存储堆元素的数组 */
	int Size; /* 堆的当前元素个数 */
	int Capacity; /* 堆的最大容量 */
}; 

MaxHeap Create( int MaxSize )
{ /* 创建容量为MaxSize的空的最大堆 */
	MaxHeap H = malloc( sizeof( struct HeapStruct ) );	
	H->Elements = malloc( (MaxSize+1) * sizeof(ElementType));
    //Maxsize+1的原因是堆在第0项不放东西,我们一般存放一个哨兵节点
    //在最大堆中,哨兵节点存放一个比所有值都大的数字,
    //这样可以免去在插入过程中判断i是否 = 1
	H->Size = 0;
	H->Capacity = MaxSize;
	H->Elements[0] = MaxData;
 	/* 定义“哨兵”为大于堆中所有可能元素的值,便于以后更快操作 */
    //在最小堆中,就创建小于所有值的值
	return H;
}

最大堆插入

由于之前的哨兵节点存在,我们不需要判断i是否等于1,因为i = 1时,比所有的节点都大,应该插在树根上,而这个时候进i/2= 0,已经在堆的外面了,所以我们设置一个哨兵节点。

void Insert( MaxHeap H, ElementType item )
{ /* 将元素item 插入最大堆H,其中H->Elements[0]已经定义为哨兵 */
 	int i;
 	//先判别堆有没有满
    if ( IsFull(H) ) {
 		printf("最大堆已满");
 		return;
 	}
    //i代表了我要插入节点的位置,是H->size+1
 	i = ++H->Size; /* i指向插入后堆中的最后一个元素的位置 */
 	//i/2是父节点的位置,要循环判断是否比父节点大
    for ( ; H->Elements[i/2] < item; i/=2 ){//如果比父节点大,父节点就下来
 		H->Elements[i] = H->Elements[i/2]; /* 向下过滤结点 */
    }
    //由于之前的哨兵节点存在,我们不需要判断i是否等于1,因为i = 1时,比所有的节点都大,应该插在树根上,而这个时候进i/2= 0,已经在堆的外面了,所以我们设置一个哨兵节点
    H->Elements[i] = item; /* 将item 插入 */
}

最大堆的删除

目标:取出根节点的元素,同时删除一个节点

  • 用最后面元素来替补根的位置 //保证结构性
  • 看左右两个儿子来比较,替换,//保证有序性
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--];
 	
    //给temp找位置,parent来指示这个位置
    for( Parent=1; Parent*2<=H->Size;/*判别是否有左儿子*/ Parent=Child ) {
        
        //从左右儿子中找大的去和paraent比较
 		Child = Parent * 2;
        //先假定做儿子大,
        //Parent * 2是左儿子
        //Child+1是右儿子
 		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];
 	}
 	
    //temp位置找到,赋值
    H->Elements[Parent] = temp;
 	return MaxItem;

最大堆的建立

从后往前进行建立

//其实是之前删除的思路
void PercDown(MaxHeap H,int p)
{
    int Parent,Child;
    ElementType temp;
    
    temp =  H->Elements[p];//取出根节点的位置
    
    for(Parent = p;Parent*2<=H->Size;Parent = Child){
        Child = Parent * 2;//先假设左儿子大,Parent * 2是左儿子(完全二叉树)
        if((Child!=H->Size)&&(H->Elements[Child]<H->Elements[Child+1]))
            Child++;//如果……右儿子大
        if(X>= H->Elements[Child])
            break;//如果节点比最大的儿子大
        else
            H->Elements[Parent] = H->Elements[Child];//如果不是,纳闷替换位置
    }
    H->Elements[Parent] = temp;//赋值
}

//调整H->Data[]中的元素,使得其满足最大堆的有序性
//假设H->Size个元素已经存在H->Data[]中

void BulidHeap(MaxHeap H)
{
    int i;
    //从最后一个节点的父节点开始,到根节点1
    for(i = H->Size/2;i>0;i--)
        PercDown(H,i);
}

猜你喜欢

转载自www.cnblogs.com/lingxueqian/p/13365071.html