数据结构5-1:堆(heap)

本文详细介绍了最大堆的概念,作为优先队列的一种表示方式,最大堆利用完全二叉树结构确保每个节点的值大于其子节点。最大堆的插入和删除操作分别在O(logn)时间内完成,保证了高效性。此外,还展示了如何构建最大堆以满足有序性。代码示例展示了最大堆的创建、插入、删除和维护过程。
摘要由CSDN通过智能技术生成
说明:堆不是堆栈

什么是堆?

使用数组或链表总有不满意的地方,效率有O(n)的,那么有没有更好的方法,有没有可能用树存储?

首先,我们想到的是一个查找树(搜索树),搜索树的一个优点就是插入结点跟树的高度是有关系的。所以树的高度合适log2n,插入挺好的。但是删除呢? 删除的结点我们要删除的是最大值、最小值,要么在最左边要么在最右边。
用搜索树来做的话它的时间效率不管是插入删除它就是树的高度 。

问题是,如果我们每一次都想删除最大的,意味着每次都要删除最右边的 ,删除几次之后树就歪掉了,左边的可能保持不动,右边的就全部删光了 ,树就斜了,树的高度就不再是log2n了。这显然不是我们希望的结果,所以不能简单的用查找树(搜索树)。

我们很自然的想法 就是把这些数据放在二叉树里。

组织成什么样子呢?
最大的在树根,这样的话你要删除就把树根拿掉就行了。

其他元素呢?
有没有可能任何一个结点都是以它为根的这个子树的最大值,那么这个呢就是我们称为最大堆.

如果这样的话我们树结构能不能安排的巧妙一点、平衡一点?
最好的方法就是用完全二叉树。

堆的一个特点就是用完全二叉树来进行存储,然后完全二叉树的每一个结点都满足一个特性,任何一个结点值呢都比他左右的子树所有结点值都要来的大。所以就是它自己是这个组里最大的。

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

堆的抽象数据类型描述

最大堆的操作:

最大堆的插入

//最大堆的创建
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));
	H->Size = 0;
	H->Capacity = MaxSize; H->Elements[0] = MaxData; 
	/* 定义“哨兵”为大于堆中所有可能元素的值,便于以后更快操作 */
	return H;
}
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; /* 将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--];
	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;
}

在这里插入图片描述

最大堆的建立

习题练习:

代码

代码:

typedef struct HNode *Heap; /* 堆的类型定义 */
struct HNode {
    ElementType *Data; /* 存储元素的数组 */
    int Size;          /* 堆中当前元素个数 */
    int Capacity;      /* 堆的最大容量 */
};
typedef Heap MaxHeap; /* 最大堆 */
typedef Heap MinHeap; /* 最小堆 */

#define MAXDATA 1000  /* 该值应根据具体情况定义为大于堆中所有可能元素的值 */

MaxHeap CreateHeap( int MaxSize )
{ /* 创建容量为MaxSize的空的最大堆 */

    MaxHeap H = (MaxHeap)malloc(sizeof(struct HNode));
    H->Data = (ElementType *)malloc((MaxSize+1)*sizeof(ElementType));
    H->Size = 0;
    H->Capacity = MaxSize;
    H->Data[0] = MAXDATA; /* 定义"哨兵"为大于堆中所有可能元素的值*/

    return H;
}

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

bool Insert( MaxHeap 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;
}

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

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

ElementType DeleteMax( MaxHeap H )
{ /* 从最大堆H中取出键值为最大的元素,并删除一个结点 */
    int Parent, Child;
    ElementType MaxItem, X;

    if ( IsEmpty(H) ) {
        printf("最大堆已为空");
        return ERROR;
    }

    MaxItem = H->Data[1]; /* 取出根结点存放的最大值 */
    /* 用最大堆中最后一个元素从根结点开始向上过滤下层结点 */
    X = H->Data[H->Size--]; /* 注意当前堆的规模要减小 */
    for( Parent=1; Parent*2<=H->Size; Parent=Child ) {
        Child = Parent * 2;
        if( (Child!=H->Size) && (H->Data[Child]<H->Data[Child+1]) )
            Child++;  /* Child指向左右子结点的较大者 */
        if( X >= H->Data[Child] ) break; /* 找到了合适位置 */
        else  /* 下滤X */
            H->Data[Parent] = H->Data[Child];
    }
    H->Data[Parent] = X;

    return MaxItem;
} 

/*----------- 建造最大堆 -----------*/
void PercDown( MaxHeap H, int p )
{ /* 下滤:将H中以H->Data[p]为根的子堆调整为最大堆 */
    int Parent, Child;
    ElementType X;

    X = H->Data[p]; /* 取出根结点存放的值 */
    for( Parent=p; Parent*2<=H->Size; Parent=Child ) {
        Child = Parent * 2;
        if( (Child!=H->Size) && (H->Data[Child]<H->Data[Child+1]) )
            Child++;  /* Child指向左右子结点的较大者 */
        if( X >= H->Data[Child] ) break; /* 找到了合适位置 */
        else  /* 下滤X */
            H->Data[Parent] = H->Data[Child];
    }
    H->Data[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 );
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

爱睡觉的小馨

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值