哈夫曼树又称最优二叉树,是一类带权路径长度最短的树.
1)路径:从树的一个节点到另一个节点之间的分支构成这两个节点之间的路径.
2)路径长度:路径上的分支数目称作路径长度.
3)权:每个节点有一个实际的值,用来描述这个节点.
4)节点的带权路径长度:从该节点到根之间的路径长度与该节点的权值乘积.
5)树的带权路径长度:树中所有叶子节点的带权路径长度之和.
哈夫曼树的构造方法
1)为每个值创建一个节点,我们称为节点群.
2)从节点群中选出两个最小的节点,组成一棵树.(将选出的节点从节点群中除去)
3)将新树的根节点放到节点群中.
4)重复2)3),直到节点群中只剩一个节点.
void h_create(Tree * tree, int arr[], int size) { Node ** p = (Node **)malloc( sizeof(Node*) * size ); int i, j; int k1, k2; Node * node; for ( i = 0; i < size; i++ )//创建节点,将节点地址放到p数组里 { p[i] = (Node *)malloc( sizeof(Node) ); p[i]->data = arr[i]; p[i]->left = p[i]->right = NULL; } for ( i = 0; i < size - 1; i++ ) { k1 = -1; for ( j = 0; j < size; j++ )//将k1,k2赋值为第一个和第二个不为NULL的下标 { if ( p[j] != NULL && k1 == -1)//这里把k1设置成最小值,k2次小值 { k1 = j; continue; } else if ( p[j] != NULL ) { k2 = j; break; } } for ( j = k1 + 1; j < size; j++ )//在数组中找到两个最小的值,第一次比较是的是k1和k2 { if ( p[j] != NULL ) { if ( p[k1]->data > p[j]->data )//如果k1的值大于j的值,将k1变成j,k2变成k1 { k2 = k1; k1 = j; } else if ( p[k2]->data > p[j]->data )//如果k2的值大于j的值,则将k2变成j { k2 = j; } } } node = (Node *)malloc( sizeof(Node) );//创建一个新节点,它的左右子树指向两个最小节点 node->data = p[k1]->data + p[k2]->data; node->left = p[k1]; node->right = p[k2]; p[k1] = node;//将新节点放入数组中k1的位置,将k2置空 p[k2] = NULL; } free(p); tree->root = node;//构建哈夫曼树完成 }
哈夫曼树权值(求的是所有叶子节点*叶子节点的路径长度的和)
1)通过递归找到叶子节点,并记录路径长度.
2)返回路径长度与权值乘积(递归有问题的同学可以把哈夫曼树画出来,根据代码走两次叶子节点就会明白了)
static int power_haffman(Node * current, int len) { if ( current->left == NULL && current->right == NULL )//当到达带权节点时,返回权值*路径 return current->data * len; else return power_haffman(current->left, len + 1) + power_haffman(current->right, len + 1);//递归计算每个路径长度,将权值与路径长度的乘积相加 }
哈夫曼编码
1)通过递归找到叶子节点,同时记录编码,左0右1(建一个数组,数组的长度最小大于元素个数减一)
2)找到叶子节点后输出,并找下一个叶子节点(在找下个叶子节点时会改变数组里的编码)
static void code_haffman(Node * current, int len) { static int a[10]; int i; if ( current->left == NULL && current->right == NULL )//当到达元素节点时输出数组里的信息 { printf("%d : ", current->data); for ( i = 0; i < len; i++) printf("%d", a[i]); printf(" "); } else//当没有到达元素节点时,向左走,往数组里写0,右走写1 { a[len] = 0; code_haffman(current->left, len + 1); a[len] = 1; code_haffman(current->right, len + 1); } }
源码
htree.h
#ifndef _HAFFMAN_H #define _HAFFMAN_H typedef struct node { int data; struct node * left; struct node * right; } Node; typedef struct tree { Node * root; } Tree; void h_create(Tree * tree, int arr[], int size);//构造哈夫曼树 int h_value(Tree * tree);//计算权值 void h_coding(Tree * tree);//哈夫曼编码 void h_print(Tree * tree);//输出 #endif //_HAFFMAN_H
htree.c
#include <stdio.h> #include <stdlib.h> #include <assert.h> #include "htree.h" static int power_haffman(Node * current, int len);//计算权值 static void code_haffman(Node * current, int len);//计算哈夫曼编码并输出 static void treaverse_haffman(Node * x); void h_create(Tree * tree, int arr[], int size) { Node ** p = (Node **)malloc( sizeof(Node*) * size ); int i, j; int k1, k2; Node * node; for ( i = 0; i < size; i++ )//创建节点,将节点地址放到p数组里 { p[i] = (Node *)malloc( sizeof(Node) ); p[i]->data = arr[i]; p[i]->left = p[i]->right = NULL; } for ( i = 0; i < size - 1; i++ ) { k1 = -1; for ( j = 0; j < size; j++ )//将k1,k2赋值为第一个和第二个不为NULL的下标 { if ( p[j] != NULL && k1 == -1)//这里把k1设置成最小值,k2次小值 { k1 = j; continue; } else if ( p[j] != NULL ) { k2 = j; break; } } for ( j = k1 + 1; j < size; j++ )//在数组中找到两个最小的值,第一次比较是的是k1和k2 { if ( p[j] != NULL ) { if ( p[k1]->data > p[j]->data )//如果k1的值大于j的值,将k1变成j,k2变成k1 { k2 = k1; k1 = j; } else if ( p[k2]->data > p[j]->data )//如果k2的值大于j的值,则将k2变成j { k2 = j; } } } node = (Node *)malloc( sizeof(Node) );//创建一个新节点,它的左右子树指向两个最小节点 node->data = p[k1]->data + p[k2]->data; node->left = p[k1]; node->right = p[k2]; p[k1] = node;//将新节点放入数组中k1的位置,将k2置空 p[k2] = NULL; } free(p); tree->root = node;//构建哈夫曼树完成 } int h_value(Tree * tree) { if ( tree->root == NULL )//空树返回0 return 0; else return power_haffman(tree->root, 0); } void h_coding(Tree * tree) { code_haffman(tree->root, 0); } void h_print(Tree * tree)//非递归方法遍历二叉树,中序遍历 { treaverse_haffman(tree->root); } static int power_haffman(Node * current, int len) { if ( current->left == NULL && current->right == NULL )//当到达带权节点时,返回权值*路径 return current->data * len; else return power_haffman(current->left, len + 1) + power_haffman(current->right, len + 1);//递归计算每个路径长度,将权值与路径长度的乘积相加 } static void code_haffman(Node * current, int len) { static int a[10]; int i; if ( current->left == NULL && current->right == NULL )//当到达元素节点时输出数组里的信息 { printf("%d : ", current->data); for ( i = 0; i < len; i++) printf("%d", a[i]); printf(" "); } else//当没有到达元素节点时,向左走,往数组里写0,右走写1 { a[len] = 0; code_haffman(current->left, len + 1); a[len] = 1; code_haffman(current->right, len + 1); } } static void treaverse_haffman(Node * x) { if ( x == NULL ) return; treaverse_haffman(x->left); printf("%d ", x->data); treaverse_haffman(x->right); }