a,路径和路径长度
如果在一棵树中有一个节点序列k1,k2,...,kj,因此ki是ki + 1的父代(1 <= i 从k1到kj的分支数称为这两点之间的路径长度,它等于路径上的节点数减去1.
b. 节点权重和加权路径长度
在许多应用程序中,树中的节点通常被赋予具有一定含义的实数. 我们称这个实数为节点的权重(例如下面树中的蓝色数字表示该节点的权重)
节点的加权路径长度定义为从根节点到该节点的路径长度与该节点上的权重的乘积.
c. 树的加权路径长度
树的加权路径长度定义为树中所有叶节点的加权路径长度之和,公式为:
其中,n表示叶节点的数量,wi和li分别表示叶节点ki的权重和从根节点到ki的路径长度.
下图中的树的加权路径长度WPL = 9 x 2 + 12 x 2 + 15 x 2 + 6 x 3 + 3 x 4 + 5 x 4 = 122
d. 霍夫曼树
霍夫曼树也称为最佳二叉树. 它是由n个加权叶节点组成的所有二叉树中加权路径长度WPL最小的二叉树.
下图是霍夫曼树的.
假设权重为n,则构造的霍夫曼树具有n个叶节点. 将n个权重设置为w1,w2,...,wn,则霍夫曼树的构造规则为:
(1)将w1,w2,...,wn视为n棵树的森林(每棵树只有一个节点);
(2)在林中,选择要合并的根节点权重最小的两棵树
(3)从森林中删除两棵选定的树,然后将新树添加到森林中;
(4)重复步骤(2)和(3),直到森林中只剩下一棵树. 这棵树是获得的霍夫曼树.
例如: 要从下图中的六个加权叶子节点构建霍夫曼树,步骤如下:
注意: 为了使生成的霍夫曼树的结构尽可能唯一,通常指定生成的霍夫曼树中每个节点的左子树根节点的权重小于或等于权重右子树的根节点.
具体算法如下:
/**
* 创建哈夫曼树
*/
PtrHuffman createHuffmanTree(ElemType arr[]){
PtrHuffman ptrArr[LENGTH];
PtrHuffman ptr,pRoot=NULL;
for (int i = ; i < LENGTH; i++){ //初始化结构体指针数组,数组中每一个元素为一个结构体指针类型
ptr = (PtrHuffman)malloc(sizeof(HuffmanTreeNode));
ptr->data = arr[i];
ptr->left = ptr->right = NULL;
ptrArr[i] = ptr;
}
for(i = ; i < LENGTH; i++){ //进行 n-1 次循环建立哈夫曼树
//k1表示森林中具有最小权值的树根结点的下标,k2为次最小的下标
int k1 = -, k2;
for(int j = ; j < LENGTH; j++){
if (ptrArr[j] != NULL && k1 == -){
k1 = j;
continue;
}
if (ptrArr[j] != NULL){
k2 = j;
break;
}
}
//将指针数组中的指针指向的最小值赋值给索引号为k1的,次小值赋值给索引号为k2的
for (j = k2; j < LENGTH; j++){
if(ptrArr[j] != NULL){
if(ptrArr[j]->data < ptrArr[k1]->data){
k2 = k1;
k1 = j;
}else if(ptrArr[j]->data < ptrArr[k2]->data){
k2 = j;
}
}
}
//由最小权值树和次最小权值树建立一棵新树,pRoot指向树根结点
pRoot = (PtrHuffman)malloc(sizeof(HuffmanTreeNode));
pRoot->data = ptrArr[k1]->data + ptrArr[k2]->data;
pRoot->left = ptrArr[k1];
pRoot->right = ptrArr[k2];
ptrArr[k1] = pRoot; //将指向新树的指针赋给ptrArr指针数组中k1位置
ptrArr[k2] = NULL; //k2位置为空
}
return pRoot;
}
在电报通信中,消息以二进制0和1序列传输,每个字符对应一个二进制代码. 为了缩短消息的总长度,采用不等长编码方法构造霍夫曼树.
p>
每个字符的频率作为字符节点的权重被赋予叶节点,每个分支节点的左,右分支分别用0和1编码,从树的根节点到每个叶节点的路径上
分支的0和1编码序列等于叶节点的二进制编码. 上面显示的霍夫曼代码如下:
a的编码为: 00
b的编码为: 01
c的编码为: 100
d的编码为: 1010
e的编码为: 1011
f的编码为: 11
以上霍夫曼树用作具体示例,以详细的程序演示霍夫曼树的操作:
/** 哈夫曼树编码 **/
#include
#include
#define LENGTH 6
typedef int ElemType;
typedef struct HuffmanTreeNode{
ElemType data; //哈夫曼树中节点的权值
struct HuffmanTreeNode* left;
struct HuffmanTreeNode* right;
}HuffmanTreeNode,*PtrHuffman;
/**
* 创建哈夫曼树
*/
PtrHuffman createHuffmanTree(ElemType arr[]){
PtrHuffman ptrArr[LENGTH];
PtrHuffman ptr,pRoot=NULL;
for (int i = ; i < LENGTH; i++){ //初始化结构体指针数组,数组中每一个元素为一个结构体指针类型
ptr = (PtrHuffman)malloc(sizeof(HuffmanTreeNode));
ptr->data = arr[i];
ptr->left = ptr->right = NULL;
ptrArr[i] = ptr;
}
for(i = ; i < LENGTH; i++){ //进行 n-1 次循环建立哈夫曼树
//k1表示森林中具有最小权值的树根结点的下标,k2为次最小的下标
int k1 = -, k2;
for(int j = ; j < LENGTH; j++){
if (ptrArr[j] != NULL && k1 == -){
k1 = j;
continue;
}
if (ptrArr[j] != NULL){
k2 = j;
break;
}
}
//将指针数组中的指针指向的最小值赋值给索引号为k1的,次小值赋值给索引号为k2的
for (j = k2; j < LENGTH; j++){
if(ptrArr[j] != NULL){
if(ptrArr[j]->data < ptrArr[k1]->data){
k2 = k1;
k1 = j;
}else if(ptrArr[j]->data < ptrArr[k2]->data){
k2 = j;
}
}
}
//由最小权值树和次最小权值树建立一棵新树,pRoot指向树根结点
pRoot = (PtrHuffman)malloc(sizeof(HuffmanTreeNode));
pRoot->data = ptrArr[k1]->data + ptrArr[k2]->data;
pRoot->left = ptrArr[k1];
pRoot->right = ptrArr[k2];
ptrArr[k1] = pRoot; //将指向新树的指针赋给ptrArr指针数组中k1位置
ptrArr[k2] = NULL; //k2位置为空
}
return pRoot;
}
/**
* 计算哈夫曼树带权路径长度WPL
*/
ElemType calculateWeightLength(PtrHuffman &ptrTree,int len){
if(ptrTree==NULL){ //空树返回0
return ;
}else{
if(ptrTree->left==NULL && ptrTree->right==NULL){ //访问到叶子节点
return ptrTree->data * len;
}else{
return calculateWeightLength(ptrTree->left,len+) + calculateWeightLength(ptrTree->right,len+); //向下递归计算
}
}
}
/**
* 哈夫曼树编码(叶子节点按中序方式依次打印其编码)
*/
void HuffmanCoding(PtrHuffman &ptrTree,int len){
//静态局部变量相当于全局变量(只是只有在这个函数中能访问,但是生命周期是和全局变量差不多的)函数退出之后变量还在,而且只在第一次进入的时候做初始化,以后会跳过初始化语句,保留原来的值
static int arr[];
if(ptrTree != NULL){
if(ptrTree->left==NULL && ptrTree->right==NULL){
printf("结点权值为%d的编码: ", ptrTree->data);
for(int i = ; i < len; i++){
printf("%d", arr[i]);
}
printf("\n");
}else{
arr[len] = ;
HuffmanCoding(ptrTree->left,len+);
arr[len] = ;
HuffmanCoding(ptrTree->right,len+);
}
}
}
/**
* 打印哈夫曼树中各个节点的孩子节点
* 若为叶子节点,则只显示提示信息
* @param node 需要显示孩子节点的父节点
*/
void printHuffmanTreeChildNode(PtrHuffman node){
if(node->left == NULL && node->right == NULL){
printf("x=%d是哈夫曼树中的叶子节点",node->data);
printf("\n\n");
return;
}
if(node->left != NULL){
printf("x=%d在哈夫曼树中的左孩子节点是lchild=%d",node->data,node->left->data);
printf("\n");
}
if(node->right != NULL){
printf("x=%d在哈夫曼树中的右孩子节点是rchild=%d",node->data,node->right->data);
printf("\n");
}
printf("\n");
}
/**
* 中序打印哈夫曼树的节点
*/
void midOrderprintHuffmanTreeNode(PtrHuffman &pRoot){
if(pRoot==NULL){
return;
}else{
midOrderprintHuffmanTreeNode(pRoot->left);
printf("%d ",pRoot->data);
midOrderprintHuffmanTreeNode(pRoot->right);
}
}
/**
* 先序打印哈夫曼树的节点
*/
void PreOrderprintHuffmanTreeNode(PtrHuffman &pRoot){
if(pRoot==NULL){
return;
}else{
printHuffmanTreeChildNode(pRoot); //依次打印哈夫曼树中各个节点的孩子节点
PreOrderprintHuffmanTreeNode(pRoot->left);
PreOrderprintHuffmanTreeNode(pRoot->right);
}
}
/**
* 测试程序入口
*/
int main(){
ElemType arr[] = {,,,,,};
PtrHuffman pRoot = createHuffmanTree(arr); //返回指向哈夫曼树根节点的指针
printf("==========中序打印哈夫曼树节点数据==========\n");
midOrderprintHuffmanTreeNode(pRoot);
printf("\n\n");
printf("==========先序打印哈夫曼树节点关系==========\n");
PreOrderprintHuffmanTreeNode(pRoot);
printf("==========计算带权路径长度==========\n");
printf("WeightLength=%d\n",calculateWeightLength(pRoot,));
printf("\n");
printf("==========各节点的哈夫曼树编码==========\n");
HuffmanCoding(pRoot,);
fprintf(stdout,"\n");
return ;
}
运行结果的屏幕截图:
更多有关使用C语言的数据结构实现霍夫曼树的相关文章
0. 数据形分析系列数据结构系列文章数据形分析: 数组. 单链表. 双链表介绍和C ++模板实现数据形分析: 堆栈和C ++模板实现数据结构介绍图形分析: 队列和C ++模板实现的详细说明...
本章介绍霍夫曼树. 与过去一样,本文将首先简要介绍Huffman树的理论知识,然后给出C语言的实现. C ++和Java版本的实现将在后面给出: 实现尽管语言不同,但是原理完全相同,请选择其中一种进行理解. 如果...
上一章介绍了霍夫曼树的基本概念,并在C语言中实现了霍夫曼树. 本章是霍夫曼树的C ++实现. 目录1. Huffman树简介2. Huffman树Manman树的图形分析3. Huffman树的基本操作4.完整的Huffman树源代码已复制...
霍夫曼树分别用C和C ++实现. 本章给出了霍夫曼树的Java版本. 目录1. Huffman树的简介2. Huffman树的图形分析3. Huffman树Man树的基本操作4.再现了Huffman树的完整源代码,请注明出处: htt ..
代码列表如下: #pragma一次#include #include“ stdlib.h” #include ...
版权声明: 本文摘自Wang Lei的博客,未经作者许可,严禁转载. 最近,我在忙于开发新版本. 另外,我正在审查C语言. 这有点慢,但是自从我写了它之后,我一定会尽力组织这部分并分享它...
第6章树和二叉树-教科书的源代码部分中的霍夫曼树(HuffmanTree)--颜为民. 吴维民版源代码使用说明链接
霍夫曼树,由发明人命名,也称为最优树c哈夫曼树编码,是加权路径最短的一种二叉树,主要用于数据压缩传输. 霍夫曼树的构造过程相对比较简单,要了解霍夫曼数,首先必须了解霍夫曼编码. 对于一组不同的频率...
//文件#include #include #include typedef ...
中有QT实现的接口
Java数据结构和算法(4)霍夫曼树的数据结构和算法目录()霍夫曼树也称为最优二叉树,即霍夫曼树...
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/jisuanjixue/article-232227-1.html