写本题的时候距离学习堆已经过去了一个月,很多东西都忘得差不多了,本博客为思路重新整理。
堆的主要操作
- 首先,堆的出现体现了二叉树的数组储存形式的应用,与传统二叉树相比,这种完全二叉树利用数组下标的特性免去了左右子树的繁复。这种繁复第一体现在树的结构上,第二体现在应用的时候只有单向性,不能快速查找其父节点。
- 由此我们首先谈堆的结构。
typedef int ElementType;
typedef struct MinHeap *Heap;
struct MinHeap{
ElementType *Elements;
int Size;
int MaxSize;
};
此处应用了一个数组Elements,这种体现方式便于后续分配内存空间的式子表示。Size就是当前二叉树上有几个元素了,也能实时体现下一个元素该插入到哪里,在实现上就是数组下标。MaxSize就是数组的最大值,即二叉树最多增加到哪里。
与传统的二叉树相比,数组的储存结构免去了左右子树但是增加了当前数组元素个数的记录以及元素最大个数。
3. 空堆的建立
Heap CreateHeap(int Max)
{
Heap H=(Heap)malloc(sizeof(struct MinHeap));
H->Elements=(ElementType*)malloc((Max+1)*sizeof(ElementType));
H->Size=0;
H->MaxSize=Max;
H->Elements[0]=-10002;
return H;
}
这里我忽略了哨兵的设置,同时注意哨兵是设小顶堆还是大顶堆的。
错误之处是数组分配空间的时候malloc之前的指针类型不会写。另一个编译没通过的地方,sizeof后面的是ElementType而不是int,虽然二者等价。
另外,我之前经常会忘了在结构分配空间的时候在sizeof后面写一个struct,经常只写一个MaxHeap就会导致编译错误。
4. 从空的堆开始插入新元素
void InsertHeap(Heap H)
{
int i=++H->Size;
int tmp;
while(i<=H->MaxSize){
scanf("%d",&tmp);
for(;tmp<H->Elements[i/2];i/=2)
H->Elements[i]=H->Elements[i/2];
H->Elements[i]=tmp;
i=++H->Size;
}
}
这里我一开始和乱序数组排序成堆搞混了。如果堆一开始是空的,那么插入的时候就可以保证它是有序的,具体思路就是新建一个坑然后从后往前入坑最后将新的元素填入换位后的坑,完成排序。
另,乱序数组排成堆就是从最后一个元素开始,逐个令其i/2的元素检查往下轮换就行。
通过的代码
#include<stdio.h>
#include<stdlib.h>
typedef int ElementType;
typedef struct MinHeap *Heap;
struct MinHeap{
ElementType *Elements;
int Size;
int MaxSize;
};
Heap CreateHeap(int Max)
{
Heap H=(Heap)malloc(sizeof(struct MinHeap));
H->Elements=(ElementType*)malloc((Max+1)*sizeof(ElementType));
H->Size=0;
H->MaxSize=Max;
H->Elements[0]=-10002;
return H;
}
void InsertHeap(Heap H)
{
int i=++H->Size;
int tmp;
while(i<=H->MaxSize){
scanf("%d",&tmp);
for(;tmp<H->Elements[i/2];i/=2)
H->Elements[i]=H->Elements[i/2];
H->Elements[i]=tmp;
i=++H->Size;
}
}
void PrintHeap(int Num,Heap H)
{
int i=1;
int addr;
for(;i<=Num;i++){
scanf("%d",&addr);
int j=addr;
for(;j>1;j/=2){
printf("%d ",H->Elements[j]);
}
printf("%d\n",H->Elements[j]);
}
}
int main()
{
int N,M;
scanf("%d%d",&N,&M);
Heap H=CreateHeap(N);
InsertHeap(H);
PrintHeap(M,H);
return 0;
}