1、基本概念
a、路径和路径长度
若在一棵树中存在着一个结点序列 k1,k2,……,kj, 使得 ki是ki+1 的双亲(1<=i<j),则称此结点序列是从 k1 到 kj 的路径。
从 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 最小的二叉树。
如下图为一哈夫曼树示意图。

2、构造哈夫曼树
假设有n个权值,则构造出的哈夫曼树有n个叶子结点。 n个权值分别设为 w1、w2、…、wn,则哈夫曼树的构造规则为:
(1) 将w1、w2、…,wn看成是有n 棵树的森林(每棵树仅有一个结点);
(2) 在森林中选出两个根结点的权值最小的树合并,作为一棵新树的左、右子树,且新树的根结点权值为其左、右子树根结点权值之和;
(3)从森林中删除选取的两棵树,并将新树加入森林;
(4)重复(2)、(3)步,直到森林中只剩一棵树为止,该树即为所求得的哈夫曼树。
如:对 下图中的六个带权叶子结点来构造一棵哈夫曼树,步骤如下:

注意:为了使得到的哈夫曼树的结构尽量唯一,通常规定生成的哈夫曼树中每个结点的左子树根结点的权小于等于右子树根结点的权。
具体算法如下:
-
- struct BTreeNode* CreateHuffman(ElemType a[], int n)
- {
- int i, j;
- struct BTreeNode **b, *q;
- b = malloc(n*sizeof(struct BTreeNode));
- for (i = 0; i < n; i++)
- {
- b[i] = malloc(sizeof(struct BTreeNode));
- b[i]->data = a[i];
- b[i]->left = b[i]->right = NULL;
- }
- for (i = 1; i < n; i++)
- {
-
- int k1 = -1, k2;
- for (j = 0; j < n; j++)
- {
- if (b[j] != NULL && k1 == -1)
- {
- k1 = j;
- continue;
- }
- if (b[j] != NULL)
- {
- k2 = j;
- break;
- }
- }
- for (j = k2; j < n; j++)
- {
- if (b[j] != NULL)
- {
- if (b[j]->data < b[k1]->data)
- {
- k2 = k1;
- k1 = j;
- }
- else if (b[j]->data < b[k2]->data)
- k2 = j;
- }
- }
-
- q = malloc(sizeof(struct BTreeNode));
- q->data = b[k1]->data + b[k2]->data;
- q->left = b[k1];
- q->right = b[k2];
-
- b[k1] = q;
- b[k2] = NULL;
- }
- free(b);
- return q;
- }
3、哈夫曼编码
在电报通信中,电文是以二进制的0、1序列传送的,每个字符对应一个二进制编码,为了缩短电文的总长度,采用不等长编码方式,构造哈夫曼树,
将每个字符的出现频率作为字符结点的权值赋予叶子结点,每个分支结点的左右分支分别用0和1编码,从树根结点到每个叶子结点的路径上
所经分支的0、1编码序列等于该叶子结点的二进制编码。如上文所示的哈夫曼编码如下:

a 的编码为:00
b 的编码为:01
c 的编码为:100
d 的编码为:1010
e 的编码为:1011
f 的编码为:11
4、哈夫曼树的操作运算
以上文的哈夫曼树作为具体实例,用详细的程序展示哈夫曼树的操作运算
- #include<stdio.h>
- #include<stdlib.h>
- typedef int ElemType;
- struct BTreeNode
- {
- ElemType data;
- struct BTreeNode* left;
- struct BTreeNode* right;
- };
-
-
- void PrintBTree_int(struct BTreeNode* BT)
- {
- if (BT != NULL)
- {
- printf("%d", BT->data);
- if (BT->left != NULL || BT->right != NULL)
- {
- printf("(");
- PrintBTree_int(BT->left);
- if (BT->right != NULL)
- printf(",");
- PrintBTree_int(BT->right);
- printf(")");
- }
- }
- }
-
-
- struct BTreeNode* CreateHuffman(ElemType a[], int n)
- {
- int i, j;
- struct BTreeNode **b, *q;
- b = malloc(n*sizeof(struct BTreeNode));
- for (i = 0; i < n; i++)
- {
- b[i] = malloc(sizeof(struct BTreeNode));
- b[i]->data = a[i];
- b[i]->left = b[i]->right = NULL;
- }
- for (i = 1; i < n; i++)
- {
-
- int k1 = -1, k2;
- for (j = 0; j < n; j++)
- {
- if (b[j] != NULL && k1 == -1)
- {
- k1 = j;
- continue;
- }
- if (b[j] != NULL)
- {
- k2 = j;
- break;
- }
- }
- for (j = k2; j < n; j++)
- {
- if (b[j] != NULL)
- {
- if (b[j]->data < b[k1]->data)
- {
- k2 = k1;
- k1 = j;
- }
- else if (b[j]->data < b[k2]->data)
- k2 = j;
- }
- }
-
- q = malloc(sizeof(struct BTreeNode));
- q->data = b[k1]->data + b[k2]->data;
- q->left = b[k1];
- q->right = b[k2];
-
- b[k1] = q;
- b[k2] = NULL;
- }
- free(b);
- return q;
- }
-
-
- ElemType WeightPathLength(struct BTreeNode* FBT, int len)
- {
- if (FBT == NULL)
- return 0;
- else
- {
- if (FBT->left == NULL && FBT->right == NULL)
- return FBT->data * len;
- else
- return WeightPathLength(FBT->left,len+1)+WeightPathLength(FBT->right,len+1);
- }
- }
-
-
- void HuffManCoding(struct BTreeNode* FBT, int len)
- {
- static int a[10];
- if (FBT != NULL)
- {
- if (FBT->left == NULL && FBT->right == NULL)
- {
- int i;
- printf("结点权值为%d的编码:", FBT->data);
- for (i = 0; i < len; i++)
- printf("%d", a[i]);
- printf("\n");
- }
- else
- {
- a[len] = 0;
- HuffManCoding(FBT->left, len + 1);
- a[len] = 1;
- HuffManCoding(FBT->right, len + 1);
- }
- }
- }
-
-
- void main()
- {
- int n, i;
- ElemType* a;
- struct BTreeNode* fbt;
- printf("从键盘输入待构造的哈夫曼树中带权叶子结点数n:");
- while(1)
- {
- scanf("%d", &n);
- if (n > 1)
- break;
- else
- printf("重输n值:");
- }
- a = malloc(n*sizeof(ElemType));
- printf("从键盘输入%d个整数作为权值:", n);
- for (i = 0; i < n; i++)
- scanf(" %d", &a[i]);
- fbt = CreateHuffman(a, n);
- printf("广义表形式的哈夫曼树:");
- PrintBTree_int(fbt);
- printf("\n");
- printf("哈夫曼树的带权路径长度:");
- printf("%d\n", WeightPathLength(fbt, 0));
- printf("树中每个叶子结点的哈夫曼编码:\n");
- HuffManCoding(fbt, 0);
- }
运行结果: