7、ハフマン木
7.1、ハフマン木の概要
また、最適なバイナリツリーとして知られているハフマンツリーは、それは、n個のバイナリツリーのリーフノードの構成を有するすべての権利、二分木の最小加重経路長です。
ツリーのいわゆる加重経路長は、ルートノードへの経路の長さを乗じたツリー値のリーフノードのすべての権利です。
二分木のルートノードから大きな重量は、ほぼ最適なバイナリツリーです。
加重経路長経路ツリー(] WPL)は、ルート・ノードから各経路の長さであり、WPL =(W1 * L1 + W2 * L2 + W3 * L3 + ... + Wnを* LN)として記録しますN重みWi(iは1,2 =、... n)のバイナリツリーは、N個のリーフノード、経路長のLi(iは1,2 =、に対応するリーフノードを有する構成... Nの)。
()WPL = 9×2 + 4×2 + 2×2 + 5 * 2 = 40
(B)WPL = 9 + 4 * 2 + 2 * 3 + 5×3 = 38
(C)WPL = 9 + 5 * 2 + 4 * 3 + 2 * 3 = 37
だから、(c)は、最適なバイナリツリーです。
7.2、ビルドハフマンプロセス分析
数値のセットを考えると、数字のこれらのセットは、次のように命じ、ソートされています。
(1)ルート最小重量2つのバイナリを削除
(2)2つのバイナリ二つの新しいバイナリサブツリーの前に取り出し、新しいバイナリツリー、からなります
ルートノードの重量(3)新しいバイナリツリーは最初の2つのバイナリツリーのルートの加重和であり、そして
図:
コード7.3、ハフマン木の実装
(1)ハフマンツリーデータ構造を定義します。
パブリック クラスノード<E> { そして日付; ダブル重量; ノードleftChild; ノードrightChild; public Node(E data, double weight) { super(); this.data = data; this.weight = weight; } public String toString() { return "Node[data=" + data + ", weight=" + weight + "]"; } }
(2)根据树的结点形成哈夫曼树:
/** * 构造哈夫曼树 * * @param nodes * 节点集合 * @return 构造出来的哈夫曼树的根节点 */ private static Node createTree(List<Node> nodes) { // 只要nodes数组中还有2个以上的节点 while (nodes.size() > 1) { quickSort(nodes); //获取权值最小的两个节点 Node left = nodes.get(nodes.size()-1); Node right = nodes.get(nodes.size()-2); //生成新节点,新节点的权值为两个子节点的权值之和 Node parent = new Node(null, left.weight + right.weight); //让新节点作为两个权值最小节点的父节点 parent.leftChild = left; parent.rightChild = right; //删除权值最小的两个节点 nodes.remove(nodes.size()-1); nodes.remove(nodes.size()-1); //将新节点加入到集合中 nodes.add(parent); } return nodes.get(0); } /** * 将指定集合中的i和j索引处的元素交换 * * @param nodes * @param i * @param j */ private static void swap(List<Node> nodes, int i, int j) { Node tmp; tmp = nodes.get(i); nodes.set(i, nodes.get(j)); nodes.set(j, tmp); } /** * 实现快速排序算法,用于对节点进行排序 * * @param nodes * @param start * @param end */ private static void subSort(List<Node> nodes, int start, int end) { if (start < end) { // 以第一个元素作为分界值 Node base = nodes.get(start); // i从左边搜索,搜索大于分界值的元素的索引 int i = start; // j从右边开始搜索,搜索小于分界值的元素的索引 int j = end + 1; while (true) { // 找到大于分界值的元素的索引,或者i已经到了end处 while (i < end && nodes.get(++i).weight >= base.weight) ; // 找到小于分界值的元素的索引,或者j已经到了start处 while (j > start && nodes.get(--j).weight <= base.weight) ; if (i < j) { swap(nodes, i, j); } else { break; } } swap(nodes, start, j); //递归左边子序列 subSort(nodes, start, j - 1); //递归右边子序列 subSort(nodes, j + 1, end); } } public static void quickSort(List<Node> nodes){ subSort(nodes, 0, nodes.size()-1); } //广度优先遍历 public static List<Node> breadthFirst(Node root){ Queue<Node> queue = new ArrayDeque<Node>(); List<Node> list = new ArrayList<Node>(); if(root!=null){ //将根元素加入“队列” queue.offer(root); } while(!queue.isEmpty()){ //将该队列的“队尾”元素加入到list中 list.add(queue.peek()); Node p = queue.poll(); //如果左子节点不为null,将它加入到队列 if(p.leftChild != null){ queue.offer(p.leftChild); } //如果右子节点不为null,将它加入到队列 if(p.rightChild != null){ queue.offer(p.rightChild); } } return list; }
(3)测试方法:
public static void main(String[] args) { List<Node> nodes = new ArrayList<Node>(); nodes.add(new Node("A", 40.0)); nodes.add(new Node("B", 8.0)); nodes.add(new Node("C", 10.0)); nodes.add(new Node("D", 30.0)); nodes.add(new Node("E", 10.0)); nodes.add(new Node("F", 2.0)); Node root = HuffmanTree.createTree(nodes); System.out.println(breadthFirst(root)); }
结果:
[Node[data=null, weight=100.0], Node[data=A, weight=40.0], Node[data=null, weight=60.0], Node[data=null, weight=30.0], Node[data=D, weight=30.0], Node[data=C, weight=10.0], Node[data=null, weight=20.0], Node[data=null, weight=10.0], Node[data=E, weight=10.0], Node[data=F, weight=2.0], Node[data=B, weight=8.0]]