哈夫曼树(附带C语言和python代码)

本文介绍了哈夫曼树的基本概念,包括其定义、节点、路径和带权性质。重点讲解了如何用C语言和Python实现哈夫曼树的构建过程,通过代码展示了寻找权值最小和次小节点以及合并节点的操作。
摘要由CSDN通过智能技术生成

 1、了解哈夫曼树是什么

哈夫曼树,又称最优二叉树,是一种带权路径长度最短的树。

1.1、二叉树

它由节点组成,每个节点最多有两个子节点,分别称为左子节点和右子节点。每个子节点可以为空,也可以包含数据或者其他引用。

1.2、节点

节点通常指代数据结构中的一个元素或者一个数据点。这里就是指的1,2,3,4,5,6等

1.3、路径

路径是指从树的根节点到某个特定节点的一系列连接

1.4、带权

"带权"指的是每个叶节点上的权重值。

在构建哈夫曼树的过程中,根据给定的字符频率或者权重值,先构造出一个森林(各个节点的权值就是对应字符的频率),然后通过不断合并权重最小的节点,构造出唯一的哈夫曼树。

1.5、带权路径

以下是带权路径的计算公式。

比较带权路径最小,才是哈夫曼树。

根据上图可以看出,哈夫曼树跟连接方式有关系。

1.6、哈夫曼总节点数

初始的节点是n个,原理:两个子节点得到一个父节点。

那么最终得到的节点数的公式如下

                                                               2n-1

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()

  • 10
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值