堆(优先队列)priority queue
特殊的队列,取出元素的顺序是依照元素的优先权(关键字)大小,而出元素进入队列的先后顺序
操作:查找最大值(最小值),删除(最大值)
数组:
链表:
有序数组:
有序链表:
采用二叉搜索树? NO
采用完全二叉树 YES
堆的连个特性
结构性:用数组表示的完全二叉树:
有序性:任一结点的关键字是其字树所有结点的最大值(或最小值)
最大堆(MaxHeap)也称大顶堆:最大值
最小堆(MinHeap)也称“小顶堆”:最小值
从根节点到任意结点路径上结点序列的有序性
操作:插入任意一个元素,删除最 大值元素
最大堆的删除:取出根节点(最大值)元素,同时删除堆的一个结点
最大堆的建立:将已存在的N个元素按最大堆的要求存放在一个以为数组中
#include <stdio.h>
#include <stdlib.h>
//最大堆
#define MaxData 1000 //哨兵,该值应该根据具体情况定义为大于堆中所有可能元素的值
typedef int ElementType;
typedef struct HeapNode * MaxHeap;
struct HeapNode {
int Capacity; //堆的最大容量
int Size; //堆中当前元素个数
ElementType *Data; //用于存储元素的数组
};
建一个空的最大堆:
//建立一个空的最大堆
MaxHeap InitHeap(int maxsize)
{
MaxHeap H = (MaxHeap)malloc(sizeof(struct HeapNode));
H->Data = (ElementType*)malloc(sizeof(struct HeapNode) * (maxsize + 1)); //堆中的元素是从下标为1开始存储的,但是为了保证这个堆能存下maxsize个元素,所以要分配maxsize + 1个内存单元
H->Capacity = maxsize;
H->Size = 0;
H->Data[0] = MaxData; //将0下标的单元存储哨兵
for (int i = 1; i < maxsize + 1; i++)
H->Data[i] = 0;
return H;
}
判断堆是否已满或是否为空:
int IsEmpty(MaxHeap H)
{
return H->Size == 0;
}
//判断堆是否已满
int IsFull(MaxHeap H)
{
return H->Size == H->Capacity;
}
插入一个元素:
//最大堆中插入一个元素
void Insert(MaxHeap H, ElementType item)
{
int i;
if (IsFull(H))
{
printf("The heap is full\n");
return;
}
i = ++H->Size; //i为插入后堆中最后一个元素的位置
for (; H->Data[i / 2] < item; i /= 2)
H->Data[i] = H->Data[i / 2]; //循环退出时,父节点的数据已经大于item, item已经找到了正确的位置
H->Data[i] = item; //将item赋给正确的下标单元
}
删除一个元素:
ElementType Delete(MaxHeap H)
{
ElementType temp, Max;
int parent = 1, child;
if (IsEmpty(H))
{
printf("The heap is empty!\n");
return 0;
}
Max = H->Data[1]; //现将最大值即根节点的值记录下来
temp = H->Data[H->Size--]; //用最后的结点元素暂时代替根节点的数据,然后将堆的数据大小减1
//如果2 * parent > 0,那么说明parent已经是根节点了
for (; 2 * parent <= H->Size; parent = child)
{
child = 2 * parent;
//如果Child != H->Size说明child不是最后一个结点,该parent结点还有右节点,
//并且如果右节点的值大于左结点,那么child++;
if (child != H->Size && (H->Data[child + 1] < H->Data[child]))
child++; //child指向左右子节点的较大者
if (temp > H->Data[child]) break; //如果孩子结点已经小于temp了, 说明找到了合适的位置
else
H->Data[parent] = H->Data[child];
}
H->Data[parent] = temp;
return Max;
}
最大堆的建立:
1.第一步,将N个元素按输入顺序存入二叉树中,这一步需要求满足完全二叉树的结构特性,而不管其有序性
2.从最后一个右孩子的结点开始调整,做类似删除元素时的下滤操作,逐个结点调整,直至根节点
//创建一个最大堆
MaxHeap CreateMaxHeap()
{
int dt, i = 0, j;
int parent, child;
ElementType X;
MaxHeap H = InitHeap(20); //先创建一个空的最大堆,然后往里面填充元素
while (scanf_s("%d", &dt) != EOF)
{
H->Data[i + 1] = dt;
i++;
H->Size++;
}
for (j = i / 2; j >= 1; j--) //从i / 2开始逐一向下过滤
{
//下面的操作和删除元素是一模一样的,只是不用将的元素个数减一
X = H->Data[j];
for (parent = j; 2 * parent <= H->Size; parent = child)
{
child = 2 * parent;
if (child != H->Size && H->Data[child] < H->Data[child + 1])
child++;
if (H->Data[child] < X)
break;
else
H->Data[parent] = H->Data[child];
}
H->Data[parent] = X;
}
return H;
}
打印堆中的元素:
//打印堆中元素
void printHeap(MaxHeap H)
{
for (int i = 1; i <= H->Size; i++)
printf("%d ", H->Data[i]);
printf("\n");
}
先序建立树:
void PreOrderCreateHeap(MaxHeap H, int index)
{
int dt;
scanf_s("%d", &dt);
if (dt == 0)
{
return;
}
H->Size++;
H->Data[index] = dt;
printf("please enter the left child of %d :", dt);
PreOrderCreateHeap(H, 2 * index);
printf("please enter the right child of %d: ", dt);
PreOrderCreateHeap(H, 2 * index + 1);
}
主函数:
int main()
{
MaxHeap H = CreateMaxHeap();
PreOrderCreateHeap(H, 1);
printHeap(H);
return 0;
}