1、了解哈夫曼树是什么
哈夫曼树,又称最优二叉树,是一种带权路径长度最短的树。
1.1、二叉树
它由节点组成,每个节点最多有两个子节点,分别称为左子节点和右子节点。每个子节点可以为空,也可以包含数据或者其他引用。
1.2、节点
节点通常指代数据结构中的一个元素或者一个数据点。这里就是指的1,2,3,4,5,6等
1.3、路径
路径是指从树的根节点到某个特定节点的一系列连接
1.4、带权
"带权"指的是每个叶节点上的权重值。
在构建哈夫曼树的过程中,根据给定的字符频率或者权重值,先构造出一个森林(各个节点的权值就是对应字符的频率),然后通过不断合并权重最小的节点,构造出唯一的哈夫曼树。
1.5、带权路径
以下是带权路径的计算公式。
比较带权路径最小,才是哈夫曼树。
根据上图可以看出,哈夫曼树跟连接方式有关系。
1.6、哈夫曼总节点数
初始的节点是n个,原理:两个子节点得到一个父节点。
那么最终得到的节点数的公式如下
2、哈夫曼树的构建
2.1、C语言对比得到权值最小和次小
//寻找最小值和次小值的下标函数
void compare(int *s1,int *s2,stack ps,int n)
{
int min1=9999999,min2=9999999,i;//首先把这两个赋值一个比较大的数值,方面下面的比较,这里一定要够大,因为真的还有那么大
//获取最小值的下标
for(i=0;i<2*n-1;i++)
{
if((ps+i)->weight!=0 && (ps+i)->parent==-1)//如果当前权重不为NULL,并且父结点为NULL,就是孤结点
{
if(min1>(ps+i)->weight)
{
*s1=i;//获取下标
min1=(ps+i)->weight;//每次比较后获取较小值,进行下一次比较
}
}
}
//获取次小值的下标
for(i=0;i<2*n-1;i++)
{
if((ps+i)->weight!=0 && (ps+i)->parent==-1)//如果当前权重不为NULL,并且父结点为NULL,就是孤结点
{
if(min2>(ps+i)->weight && i!=*s1)
{
*s2=i;//获取下标
min2=(ps+i)->weight;//每次比较后获取较小值,进行下一次比较
}
}
}
}
2.2.、python比较权值最小和次小
def compare(weights):
nodes = [TreeNode(weight) for weight in weights]
nodes.sort(key=lambda x: x.weight) # 按权重排序
for _ in range(len(weights) - 1):
# 选择权重最小的两个节点
min1, min2 = nodes.pop(0), nodes.pop(0)
# 创建新节点,并更新权重、左右子节点和父节点
new_node = TreeNode(min1.weight + min2.weight, lchild=min1, rchild=min2)
min1.parent, min2.parent = new_node, new_node
# 将新节点插入排序后的节点列表中
nodes.append(new_node)
nodes.sort(key=lambda x: x.weight) # 重新排序
return nodes[0] # 返回树的根节点
3、C语言构建哈夫曼
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
//一开始一共有五个根节点,那么一共有9个节点。
//创建结构体
typedef struct Node
{
char name[5];
int weight;//结点权重
int parent;//父结点号
int lchild;//左孩子号
int rchild;//右孩子号
}*stack;//同时创建了一个结构体类型的指针
//寻找最小值和次小值的下标函数
void compare(int *s1,int *s2,stack ps,int n)
{
int min1=9999999,min2=9999999,i;//首先把这两个赋值一个比较大的数值,方面下面的比较,这里一定要够大,因为真的还有那么大
//获取最小值的下标
for(i=0;i<2*n-1;i++)
{
if((ps+i)->weight!=0 && (ps+i)->parent==-1)//如果当前权重不为NULL,并且父结点为NULL,就是孤结点
{
if(min1>(ps+i)->weight)
{
*s1=i;//获取下标
min1=(ps+i)->weight;//每次比较后获取较小值,进行下一次比较
}
}
}
//获取次小值的下标
for(i=0;i<2*n-1;i++)
{
if((ps+i)->weight!=0 && (ps+i)->parent==-1)//如果当前权重不为NULL,并且父结点为NULL,就是孤结点
{
if(min2>(ps+i)->weight && i!=*s1)
{
*s2=i;//获取下标
min2=(ps+i)->weight;//每次比较后获取较小值,进行下一次比较
}
}
}
}
//创建哈夫曼树初始化并填好
stack create(int leaf_weight[],char hanzi[],int n,stack ps,int *s1,int *s2)
{
int i;
ps = (stack)malloc((2*n-1) * sizeof(struct Node));//创建2*n-1个结点
//对哈夫曼树进行初始化
for(i=0;i<2*n-1;i++)
{
ps[i].parent=ps[i].lchild=ps[i].rchild=-1;//初始化为-1
if(i<n)//小于n时就赋值结点权重
{
ps[i].weight=leaf_weight[i];//把结点权重都存到数组里
ps[i].name[0]=hanzi[i];
}
else//大于0的都初始化为0
{
ps[i].weight=0;
}
}
for(i=n;i<2*n-1;i++)
{
compare(s1,s2,ps,n);
(ps+i)->weight=(ps+*s1)->weight+(ps+*s2)->weight;//该节点的权重=两个儿子的权重之和
ps[*s2].parent=ps[*s1].parent=i;//儿子的父结点是该节点
ps[i].lchild=*s1;//左儿子*s1
ps[i].rchild=*s2;//右儿子*s2
}
return ps;
}
int main() {
int leaf_weight[] = {3, 14, 8, 7, 8}; // 叶子节点权重数组
char hanzi[] = {'A', 'B', 'C', 'D', 'E'}; // 汉字数组
int n = 5; // 节点个数
stack ps = NULL; // 初始化ps为NULL
int s1, s2; // 最小值和次小值的下标
ps = create(leaf_weight, hanzi, n, ps, &s1, &s2);
// 打印输出每个结点的权重、父结点号、左孩子号、右孩子号
for (int i = 0; i < 2 * n - 1; i++) {
printf("第%d个结点权重、父结点号、左孩子号、右孩子号:\t%d\t%d\t%d\t%d\n", i+1, ps[i].weight, ps[i].parent, ps[i].lchild, ps[i].rchild);
}
// 释放动态分配的内存
free(ps);
return 0;
}
4、python构建哈夫曼
import tkinter as tk
from tkinter import messagebox
class TreeNode:
def __init__(self, weight, parent=None, lchild=None, rchild=None):
self.weight = weight # 权重
self.lchild = lchild # 左孩子号
self.rchild = rchild # 右孩子号
self.parent = parent # 父节点号
def compare(weights):
nodes = [TreeNode(weight) for weight in weights]
nodes.sort(key=lambda x: x.weight) # 按权重排序
for _ in range(len(weights) - 1):
# 选择权重最小的两个节点
min1, min2 = nodes.pop(0), nodes.pop(0)
# 创建新节点,并更新权重、左右子节点和父节点
new_node = TreeNode(min1.weight + min2.weight, lchild=min1, rchild=min2)
min1.parent, min2.parent = new_node, new_node
# 将新节点插入排序后的节点列表中
nodes.append(new_node)
nodes.sort(key=lambda x: x.weight) # 重新排序
return nodes[0] # 返回树的根节点
def draw_tree(canvas, node, x, y, h_dist, v_dist):
if node is not None:
canvas.create_oval(x-20, y-40, x+20, y+40, outline="green") # 画个椭圆,边框为黑色
canvas.create_text(x, y, text=str(node.weight)) # 在(x,y)上显示权重
if node.lchild is not None:
draw_tree(canvas, node.lchild, x - h_dist, y + v_dist, h_dist / 2, v_dist)
canvas.create_line(x, y, x - h_dist, y + v_dist, fill="green")
if node.rchild is not None:
draw_tree(canvas, node.rchild, x + h_dist, y + v_dist, h_dist / 2, v_dist)
canvas.create_line(x, y, x + h_dist, y + v_dist, fill="green")
# 创建一个窗口
win = tk.Tk()
win.title("哈夫曼树")
messagebox.showinfo("欢迎", "欢迎来到哈夫曼树世界!")
# 创建一个画布
canvas = tk.Canvas(win, width=800, height=800)
canvas.pack()
# 创建一个简单的哈夫曼树
weights = [3, 14, 8, 7, 8]
root = compare(weights)
# 绘制哈夫曼树
draw_tree(canvas, root, 400, 200, 150, 150)
# 显示窗口
win.mainloop()