哈夫曼树:
- 路径:从树根到某个节点的路径为根节点到该节点所经过的一个节点序列。路径长度为路径所含的分支数。
- 树的路径长度:从根节点到其他所有节点的路径长度之和。
- 节点的带权路径长度:从根节点开始到该节点路径长度乘该节点的权值。
- 树的带权路径长度:整个树的所有节点的带权路径长度之和。
- 哈夫曼树是带权路径最短的树,也称为最优二叉树。
构造哈夫曼树的算法:
- 将n个带有权值且只有一个叶节点的二叉树存放在数组中,从数组中取出权值最小的和次小的两个节点分别作为树的左子树和右子树,两个节点权值的和作为根节点的权值,将新构造的树插入到数组中,并删除原来的两个节点。
- 从剩下n-1个二叉树中取出最小的和次小的节点重复上述操作,直到数组中只剩下一个二叉树,此时这个二叉树就是哈夫曼树。
构造哈夫曼树的思路:
- 构造存储哈夫曼树节点的最小堆
- 实现堆删除操作
- 实现堆插入操作
- 实现构造哈夫曼树
程序实现:
- 函数和声明
typedef struct HuffmanTree* HTREE;
typedef struct HEAP* MINHEAP;
typedef HTREE ElementType;
struct HuffmanTree{
int Wight;
HTREE Lift;
HTREE Right;
};
struct HEAP{
ElementType* Data;
int Size;
int MaxSize;
};
MINHEAP CreatMinHeap(int);
MINHEAP AdjustHeap(MINHEAP);
void Insert(MINHEAP H,HTREE);
ElementType DeleteHeap(MINHEAP);
HTREE CreatHT(int);
void FreeH(MINHEAP);
- 构造存储哈夫曼树节点的最小堆
使用指针数组存放哈夫曼树节点,数组中每一个元素都是指向哈夫曼树节点的指针
在CreatMinHeap()函数中输入一组数据作为权值,然后通过调用AdjustHeap()函数将这组数据调整为最小堆形式
将AdjustHeap()函数单独写是因为在后面堆删除和堆插入操作都需要调整最小堆,代码复用,程序看起来更简洁
MINHEAP AdjustHeap(MINHEAP H){
int i,parent,child;
ElementType TEMP;
for(i=H->Size/2;i>=1;i--){
for(parent=i;2*parent<=H->Size;parent=child){
child = 2*parent;
if(child!=H->Size && (H->Data[child]->Wight>H->Data[child+1]->Wight)){
child++;
}
if(H->Data[parent]->Wight > H->Data[child]->Wight){
TEMP = H->Data[parent];
H->Data[parent] = H->Data[child];
H->Data[child] = TEMP;
}
}
}
return H;
}
MINHEAP CreatMinHeap(int maxsize){
MINHEAP H = (MINHEAP)malloc(sizeof(struct HEAP));
H->Size = 0;
H->MaxSize = maxsize;
H->Data = (ElementType*)malloc((H->MaxSize+1)*sizeof(ElementType));
H->Data[0] = NULL;
for(int i=1;i<=H->MaxSize;i++){
H->Data[i] = (ElementType)malloc(sizeof(struct HuffmanTree));
H->Data[i]->Lift = NULL;
H->Data[i]->Right = NULL;
}
printf("Input a series of wight:\n");
for(int i=1;i<=H->MaxSize;i++){
scanf("%d",&H->Data[i]->Wight);
H->Size++;
}
return AdjustHeap(H);
}
- 堆删除操作:
删除根节点并返回,因为删除后树依然要保持完全二叉树的结构,所以将最后一个节点放入删除的根节点的位置再重新排列树。
ElementType DeleteHeap(MINHEAP H){
ElementType TEMP_Data;
if(H->Size==0) exit(-1);
TEMP_Data = H->Data[1];
H->Data[1] = H->Data[H->Size--];
H = AdjustHeap(H);
return TEMP_Data;
}
- 堆插入操作:
插入到完美二叉树的最后,然后重新调整为最小堆
void Insert(MINHEAP H,HTREE T){
if(H->Size==H->MaxSize) exit(-1);
H->Size++;
H->Data[H->Size] = T;
H = AdjustHeap(H);
}
- 构造哈夫曼树函数:
HTREE CreatHT(int maxsize){
HTREE T;
int i,n;
MINHEAP H = CreatMinHeap(maxsize);
n = H->Size;
for(i=1;i<n;i++){
T = (HTREE)malloc(sizeof(struct HuffmanTree));
T->Lift = DeleteHeap(H);
T->Right = DeleteHeap(H);
T->Wight = T->Lift->Wight+T->Right->Wight;
Insert(H,T);
}
T = DeleteHeap(H);
FreeH(H);
return T;
}
最后构造完哈夫曼树后使用FreeH()函数将堆的空间释放。
实现汉夫曼树时在纠结创建堆函数中关于指向指针的指针,因为带着指向指针的指针来考虑所以很快就把自己给绕进去了,其实考虑这个问题很简单,就是把指向指针的指针就当作一个普通的指针来对待就可以了,只是它指向的内存中存放的是一个地址。