数据结构之哈夫曼树(C语言)

哈夫曼树(Huffman Tree)是一种基于字符频率优化的数据压缩算法。其数据结构仍然是二叉树

  用10,2,3,6,8五个权值作为叶子结点,构造二叉树,可能有多种不同形态。
如何能使得所有叶子结点的带权路径长度之和最小?
 带权路径长度=结点的权值 *从根结点到该节点的路径长度。
 一棵二叉树,每个叶子结点赋予权值
 WPL=(2+3)*3+(10+6+8)*2
 n个叶子结点构成的哈夫曼树,总结点个数为2n-1

具体代码如下:

定义存储结构

#include<stdio.h>
#include<stdlib.h>
typedef struct node{
	float weight;
	int parent,lChild,rChild;
}haffmanTree;

创建哈夫曼树

haffmanTree *creaHaffmanTree(int n){
	haffmanTree *ht;
	ht=(haffmanTree *)malloc(sizeof(haffmanTree)*(2*n-1));
	int k=0,i,i1,i2;
	for(i=0;i<2*n-1;++i) ht[i].lChild=ht[i].rChild=ht[i].parent=-1;
	printf("依次输入每个叶子结点的权值:\n");
	while(k<n) {
		scanf("%f",&ht[k].weight);
		++k;
	}
	while(k<2*n-1){
		i1=0;
		while(i1<k){
			if(ht[i1].parent==-1) break;//此时已经到了根结点 
			i1++;
		}
		i=i1+1;
		while(i<k){
			if(ht[i].parent==-1 &&ht[i].weight<ht[i1].weight)  
			i1=i;
			i++;
		}
		i2=0;
		while(i2<k){
			if(ht[i2].parent==-1&&i2!=i1) break;
			i2++;
		}
		i=i2+1;
		while(i<k){
			if(ht[i].parent==-1 &&i!=i1&&ht[i].weight<ht[i2].weight)
				i2=i;
			i++;
		}
		ht[k].weight=ht[i1].weight+ht[i2].weight;
		ht[k].lChild=i1;
		ht[k].rChild=i2;
		ht[i2].parent=ht[i1].parent=k;
		++k;
	}
	return ht;
}
freeHaffmanTree(haffmanTree *ht){
	free(ht);
}

这里的这个创建函数的核心逻辑是 while(k<2*n-1)之后的几个循环代码,也是是最不好理解的,我们细细拆开分析

while(k < 2 * n - 1){
    // ...
}

  这个循环会一直执行,直到k等于2 * n - 1。变量k用于跟踪当前正在处理的节点索引。由于有n个叶子节点和n-1个内部节点,总共有2n - 1个节点。

i1 = 0;
while(i1 < k){
    if(ht[i1].parent == -1) break;
    i1++;
}

这段代码使用一个内层循环找到第一个未被分配父节点(即parent == -1)的节点,这个节点是当前最小权重的节点之一。前面代码中    for(i=0;i<2*n-1;++i) ht[i].lChild=ht[i].rChild=ht[i].parent=-1;将每个结点都置为-1,表示该节点尚未被连接到树中。

i = i1 + 1;
while(i < k){
    if(ht[i].parent == -1 && ht[i].weight < ht[i1].weight)  
        i1 = i;
    i++;
}

在第二个内层循环中,代码继续寻找第二个最小权重的节点。如果找到一个未分配父节点且权重小于当前找到的最小节点i1的节点,就更新i1

i2 = 0;
while(i2 < k){
    if(ht[i2].parent == -1 && i2 != i1) break;
    i2++;
}

这段代码使用另一个内层循环找到第三个未被分配父节点的节点,这个节点是当前第二小权重的节点。

i = i2 + 1;
while(i < k){
    if(ht[i].parent == -1 && i != i1 && ht[i].weight < ht[i2].weight)
        i2 = i;
    i++;
}

在第二个内层循环中,代码继续寻找第二小权重的节点。如果找到一个未分配父节点、索引不等于i1且权重小于当前找到的第二小节点i2的节点,就更新i2

ht[k].weight = ht[i1].weight + ht[i2].weight;
ht[k].lChild = i1;
ht[k].rChild = i2;
ht[i2].parent = ht[i1].parent = k;

一旦找到两个最小权重的节点(i1i2),就创建一个新的内部节点(索引为k)。新节点的权重是两个最小节点权重的和。新节点的左右子节点分别设置为i1i2,并且更新i1i2的父节点为k

++k;

最后,更新k的值,准备下一轮循环,寻找下一对最小权重节点。

打印哈夫曼树

void printHaffmanTree(haffmanTree *ht,int n){
	printf("数组下标  结点权值  双亲下标  左孩子下标  右孩子下标\n");
	int i;
	for(i=0;i<2*n-1;++i){
		printf("%-10d%-10f%-10d%-12d%-12d\n",i,ht[i].weight,ht[i].parent,ht[i].lChild,ht[i].rChild);
	}
}

调用主函数运行

int main(){
	int n;
	printf("请输入叶子结点个数:");
	scanf("%d",&n); 
	haffmanTree *ht=creaHaffmanTree(n);
	printHaffmanTree(ht,n);
	freeHaffmanTree(ht);
	return 0;
}

运行结果

具体哈夫曼树图如图:

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值