哈夫曼树
每个结点的权值乘以与根结点的路径长度之和最小的树——最优二叉树
哈夫曼树的特点:
没有度为1的结点;
哈夫曼树的任意非叶节点的左右子树交换后仍是哈夫曼树; n个叶子结点的哈夫曼树共有2n-1个结点;
对同一组权值{w1 ,w2, …… , wn},存在不同构的哈夫曼树
哈夫曼树的构成
将二叉树按结点权值大小转化成最小堆,每次取堆定两个元素组成新的二叉树结点,插回堆中;
当进行size-1次合并,最后所得的即为哈夫曼树;
1.树结点
#include <iostream>
#include <string>
using namespace std;
#define MaxNum 64
struct TreeNode{
int Weight = 0;
TreeNode *Left = nullptr;
TreeNode *Right = nullptr;
};
2.堆结构
struct HeapNode{// 堆结构
TreeNode Data[MaxNum]; //data内放着树结构
int Size =0; //初始大小为0
};
3.堆创建函数
HeapNode * CreateHeap(){ //建树——返回指向树根节点的指针
HeapNode *H = new(HeapNode);
H->Data[0].Weight = -1; //哨兵的weight设置为-1
return H; //建堆——一次即可
}
4.堆删除函数
TreeNode *DeleteMin(HeapNode *H){ //返回堆定的指针
int Parent = 0 , Child=0;
TreeNode temp;
TreeNode *MinItem = new(TreeNode); //新建一个minitem
*MinItem = H->Data[1]; //保存堆顶
temp = H->Data[(H->Size)--]; //拿到堆底的对象,并且size-1
for(Parent=1;Parent *2 <= H->Size; Parent=Child)
{
Child= Parent*2;
//Size就是为了作为边界
if(Child!=H->Size && (H->Data[Child].Weight>H->Data[Child+1].Weight))
Child++;
if(temp.Weight<=H->Data[Child].Weight) break;
else H->Data[Parent]=H->Data[Child]; //下沉
}
//break出来说明找到合适位置
H->Data[Parent] = temp; //进行赋值
return MinItem; //返回堆定成员指针
}
5.插入堆操作
void Insert(HeapNode *H, TreeNode *item){ //将一个树成员插入堆中
int i=0;
i= ++(H->Size); //i为当前总容量
//item为指向树成员的指针
for (;item ->Weight<H->Data[i/2].Weight;i/=2) //插入最后,然后上浮
//当要插入的成员小于父节点,上浮
{
H->Data[i]=H->Data[i/2];
}
H->Data[i] = *item; //将item所指成员放入堆中
}
6.构建哈夫曼树
输入一个最小堆对象的指针;
TreeNode *Huffman(HeapNode *H){ //把堆变为哈夫曼树
TreeNode *T = nullptr; //初始空指针
int num = H->Size; //一共有num个元素
//进行n-1次合并
for(int i=0;i<num-1;i++)
{
T= new(TreeNode);
T->Left = DeleteMin(H);
T->Right = DeleteMin(H);
T->Weight=T->Left->Weight + T->Right->Weight;
Insert(H,T); //T为指针——insert(H,T)
//要用动态内存创造空间时——用指针
}
T= DeleteMin(H);
return T; //返回指向堆头的指针
//
}
总结
哈夫曼树由最小堆转化而来;
可以使得二叉树为最优二叉树;
可以避免二义性,构造编码代价最少的二叉树;