5.4.5 由完全二叉树的顺序存储结构建立二叉链式存储结构
- 由二叉树的特性5可知,结点编号规则:
 
- 根节点的编号为0
 - 编号我i的结点
 
- 左孩子的编号为2i+1
 - 右孩子的编号为2i+2
 
- 完全二叉树及其顺序存储
 
- 算法
 
publicBiTreeNodecreateBiTree(StringsqBiTree, intindex) { BiTreeNoderoot=null; if(index<sqBiTree.length()) { root=newBiTreeNode(sqBiTree.charAt(index)); root.lchild=createBiTree(sqBiTree, 2*index+1); root.rchild=createBiTree(sqBiTree, 2*index+2); } returnroot; }
5.5 哈夫曼树及哈夫曼编码
5.5.1 基本概念
- 结点间路径:从一个结点到另一个结点所经历的结点和分支序列。
 - 结点的路径长度:从根节点从该结点的路径上分支的数目。
 - 结点的权:在实际应用中,人们往往会给树中的每一个结点赋予一个具有某种实际意义的数值,这个数值被称为该结点的权值。
 - 结点的带权路径长度:该结点的路径长度与该结点的权值的乘积。
 - 树的带权路径长度:树中所有叶结点的带权路径长度之和。
 
5.5.2 最优二叉树
- 给定n个权值并作为n个叶结点,按一定规则构造一颗二叉树,使其带权路径长度达到最小值,则这棵二叉树被称为最优二叉树,也称为哈夫曼树。
 
- 同一组数据的最优二叉树不唯一,因为没有限定左右子树,并且有权值重复时,可能树的高度都不唯一,唯一的只是带权路径长度之和最小。 构建哈夫曼树的时候即可以推导出。
 
5.5.3 构建哈夫曼树
5.5.4 哈夫曼编码
- 编码诉求:对字符集进行二进制编码,使得信息的传输量最小。如果能对每一个字符用不同的长度的二进制编码,并且尽可能减少出现次数最多的字符的编码位数,则信息传送的总长度便可以达到最小。
 - 哈夫曼编码:用电文中各个字符使用的频度作为叶结点的权,构造一颗具有最小带权路径长度的哈夫曼树,若对树中的每个==左分支赋予标记0,右分支赋予标记1==,则从根节点到每个叶结点的路径上的标记连接起来就构成一个二进制串,该二进制串被称为哈夫曼编码。
 - 练习:p176
 - 已知在一个信息通信联络中使用了8个字符:a、b、c、d、e、f、g和h,每个字符的使用频度分别为:6、30、8、9、15、24、4、12,试设计各个字符的哈夫曼编码。
 
0
1
0
1
0
1
0
1
0
1
0
1
0
1
4
6
8
9
10
12
15
17
22
24
30
32
46
62
106
哈夫曼树进行译码
- 哈夫曼编码是一种前缀码,任何一个字符的编码都不是同一个字符集中另一个字符的编码的前缀。
 
- 译码过程时编码过程的逆过程。从哈夫曼树的根开始,从左到右把二进制编码的每一位进行判别,若遇到0,则选择左分支走向下一个结点;若遇到1,则选择右分支走向下一个结点,直至到达一个树叶结点,便求得响应字符。
 
5.5.5 哈夫曼编码类
哈夫曼结点类publicclassHuffmanNode { publicintweight;// 权值publicintflag; // 节点是否加入哈夫曼树的标志publicHuffmanNodeparent,lchild,rchild; // 父节点及左右孩子节点// 构造一个空节点publicHuffmanNode(){ this(0); } // 构造一个具有权值的节点publicHuffmanNode(intweight){ this.weight=weight; flag=0; parent=lchild=rchild=null; } } 哈夫曼编码类publicclassHuffmanTree { // 求哈夫曼编码的算法,w存放n个字符的权值(均>0)publicint[][] huffmanCoding(int[] W){ intn=W.length; // 字符个数intm=2*n-1; //哈夫曼树的节点数// 构造n个具有权值的节点HuffmanNode[] HN=newHuffmanNode[m]; inti=0; for (; i<n ; i++) { HN[i] =newHuffmanNode(W[i]); } // 创建哈夫曼树for (i=n; i<m ; i++) { // 在HN[0...1]选择不在哈夫曼树中,且权值weight最小的两个节点min1和min2HuffmanNodemin1=selectMin(HN,i-1); min1.flag=1; HuffmanNodemin2=selectMin(HN,i-1); min2.flag=1; // 构造 min1和min2的父节点,并修改父节点的权值HN[i] =newHuffmanNode(); min1.parent=HN[i]; min2.parent=HN[i]; HN[i].lchild=min1; HN[i].rchild=min2; HN[i].weight=min1.weight+min2.weight; } // 从叶子到根 逆向求每个字符的哈夫曼编码int[][] HuffCode=newint[n][n]; // 分配n个字符编码存储空间for (intj=0;j<n;j++){ // 编码的开始位置,初始化为数组的结尾intstart=n-1; // 从叶子节点到根,逆向求编码for(HuffmanNodec=HN[j],p=c.parent;p!=null;c=p,p=p.parent){ if(p.lchild.equals(c)){ HuffCode[j][start--]=0; }else{ HuffCode[j][start--]=1; } } // 编码的开始标志为-1,编码是-1之后的01序列HuffCode[j][start] =-1;  } returnHuffCode;  } // 在HN[0...1]选择不在哈夫曼树中,且权值weight最小的两个节点min1和min2privateHuffmanNodeselectMin(HuffmanNode[] HN, intend) { // 求 不在哈夫曼树中, weight最小值的那个节点HuffmanNodemin=HN[end]; for (inti=0; i<end; i++) { HuffmanNodeh=HN[i]; // 不在哈夫曼树中, weight最小值if(h.flag==0&&h.weight<min.weight){ min=h; } } returnmin; } }
测试类publicclassDemo02 { publicstaticvoidmain(String[] args) { // 一组权值int[] W= {6,30,8,9,15,24,4,12}; // 创建哈夫曼树HuffmanTreetree=newHuffmanTree(); // 求哈夫曼编码int[][] HN=tree.huffmanCoding(W); //打印编码System.out.println("哈夫曼编码是: "); for (inti=0; i<HN.length; i++) { System.out.print(W[i]+" "); for (intj=0; j<HN[i].length; j++) { if(HN[i][j] ==-1){ for (intk=j+1; k<HN[i].length ; k++) { System.out.print(HN[i][k]); } break; } } System.out.println(); } } }



