什麼是赫夫曼樹?
赫夫曼樹(Huffman Tree)是指給定N個權值作為N個葉子結點,構造一棵二叉樹,若該樹的帶權路徑長度達到最小。哈夫曼樹(也稱為最優二叉樹)是帶權路徑長度最短的樹,權值較大的結點離根較近。
1 public class HNode<T> 2 { 3 public HNode() 4 { 5 data = default(T); 6 weight = 0; 7 leftNode = null; 8 rightNode = null; 9 } 10 11 public HNode(T val) 12 { 13 data = val; 14 weight = 0; 15 leftNode = null; 16 rightNode = null; 17 } 18 19 /// <summary> 20 /// 權重 21 /// </summary> 22 public int weight { get; set; } 23 24 /// <summary> 25 /// 內容 26 /// </summary> 27 public T data { get; set; } 28 29 /// <summary> 30 /// 左樹 31 /// </summary> 32 public HNode<T> leftNode { get; set; } 33 34 /// <summary> 35 /// 右樹 36 /// </summary> 37 public HNode<T> rightNode { get; set; } 38 }
1 /// <summary> 2 /// 赫夫曼樹 3 /// </summary> 4 /// <typeparam name="T"></typeparam> 5 public class HTree<T> 6 { 7 /// <summary> 8 /// 樹的頭結點 9 /// </summary> 10 public HNode<T> head { get; set; } 11 12 /// <summary> 13 /// 建構函式 14 /// </summary> 15 /// <param name="val"></param> 16 public HTree(T val) 17 { 18 head = new HNode<T>(val); 19 } 20 21 public HTree() 22 { 23 head = new HNode<T>(); 24 } 25 /// <summary> 26 /// 構建樹結構 27 /// </summary> 28 /// <param name="list"></param> 29 public void build(List<T> list) 30 { 31 //判斷是否能構建樹結構 32 if (list == null || list.Count <2) 33 throw new ArgumentOutOfRangeException("params error"); 34 //分組統計 35 List<HNode<T>> nodes = new List<HNode<T>>(); 36 nodes.AddRange(from m in list group m by m into g 37 select new HNode<T> { data = g.Key,weight = g.Count()}); 38 //排序 39 nodes = nodes.OrderBy(i => i.weight).ToList(); 40 41 for (int i =1; i< nodes.Count; i++) 42 { 43 HNode<T> parentNode = new HNode<T>(); 44 if (i == 1) 45 { 46 //先取最小的兩個節點 47 parentNode.leftNode = nodes[0]; 48 parentNode.rightNode = nodes[1]; 49 parentNode.weight = nodes[0].weight + nodes[1].weight; 50 } 51 else 52 { 53 //依次取節點構建樹 54 if (head.weight >= nodes[i].weight) 55 { 56 parentNode.leftNode = head; 57 parentNode.rightNode = nodes[i]; 58 } 59 else 60 { 61 parentNode.rightNode = head; 62 parentNode.leftNode = nodes[i]; 63 } 64 parentNode.weight = head.weight + nodes[i].weight; 65 } 66 head = parentNode; 67 } 68 } 69 70 /// <summary> 71 /// 先序遍歷 72 /// </summary> 73 /// <param name="index"></param> 74 public void PreorderTraversal(HNode<T> node) 75 { 76 //遞迴的終止條件 77 if (head == null) 78 { 79 Console.WriteLine("當前樹為空"); 80 return; 81 } 82 if (node != null) 83 { 84 if(node.data != null) 85 Console.WriteLine($"{node.data} {node.weight}"); 86 PreorderTraversal(node.leftNode); 87 PreorderTraversal(node.rightNode); 88 } 89 } 90 }
測試程式碼:
1 List<string> list = new List<string>() { "A","B", "B", "C", "C", "C", "D", "D", "D", "D", "E", "E", "E", "E", "E" }; 2 HTree<string> tree = new HTree<string>(); 3 tree.build(list); 4 tree.PreorderTraversal(tree.head);
列印結果:
A 1 B 2 C 3 D 4 E 5
用這個例子現在我們看下構建的二叉樹結構: