什麼是堆:堆是一種經過排序的完全二叉樹,其中任一非終端節點的數據值均不大於(或不小於)其左孩子和右孩子節點的值;
首先,堆分爲大根堆(最大堆)和小根堆(最小堆)二種;
小根堆滿足:
(1)若根結點存在左孩子,則根結點的值小於等於左孩子結點的值;
(2)若根結點存在右孩子,則根結點的值小於等於右孩子結點的值;
(3)以左右孩子爲根的子樹又各是一個堆;
或者:
根結點(亦稱爲堆頂)的關鍵字是堆裏所有結點關鍵字中最小者的堆稱爲小根堆;
大根堆與小根堆類似,只需把小於等於改爲大於等於就得到了;
如:
哈夫曼樹:又稱最優二叉樹,是一種帶權長度最短的二叉樹,所謂樹的帶權路徑長度,就是樹中所有的葉結點的權值乘上其到根結點的路徑長度;
樹的帶權路徑長度記爲: WPL=(21*L1+W2*L2+W3*L3+...+Wn*Ln);
如:給定4個葉子結點a,b,c,d,分別帶權7,4,5,2,構造如下面所示的三棵二叉樹(還有許多種),它們的帶權路徑長度分別爲:
(1)WPL = 7*2+5*2+2*2+4*2;
(2)WPL = 7*1+5*2+2*3+4*3;
(3)WPL = 7*3+5*3+2*1+4*2;
其中(2)的WPL最小,可以驗證,它就是哈夫曼樹;
因此,在n個帶權葉子結點所構成的二叉樹中,滿二叉樹或完全二叉樹不一定是最優二叉樹,權值越大的結點離根越近的二叉樹纔是最優二叉樹;
構造哈夫曼樹:
哈夫曼編碼:
求出哈夫曼樹後,以上圖爲例,只需人爲規定左側爲0,右側爲1,那麼結點9的編碼是:00;結點5的編碼是:10。哈夫曼編碼是一種應用廣泛且非常有效的數據壓縮技術,該技術一般可將數據文件壓縮掉20%-90%,其壓縮效率取決於被壓縮文件的特徵;
Java代碼:
package huffman;
/**
*
* 二叉樹節點
*/
class Node implements Comparable
{
private int value;
private Node leftChild;
private Node rightChild;
public Node(int value)
{
this.value = value;
}
public int getValue()
{
return value;
}
public void setValue(int value)
{
this.value = value;
}
public Node getLeftChild()
{
return leftChild;
}
public void setLeftChild(Node leftChild)
{
this.leftChild = leftChild;
}
public Node getRightChild()
{
return rightChild;
}
public void setRightChild(Node rightChild)
{
this.rightChild = rightChild;
}
public int compareTo(Object o)
{
Node that = (Node) o;
double result = this.value - that.value;
return result > 0 ? 1 : result == 0 ? 0 : -1;
}
}
package huffman;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class HuffmanTreeBuilder
{
public static void main(String[] args)
{
// 方式1
List<Node> list = new ArrayList<Node>();
list.add(new Node(7));
list.add(new Node(5));
list.add(new Node(4));
list.add(new Node(2));
// 方式2
List<Node> nodes = Arrays.asList(new Node(1), new Node(3), new Node(5),
new Node(7));
Node node = HuffmanTreeBuilder.build(list);
PrintTree(node);
}
/**
*
* 構造哈夫曼樹
*
*/
private static Node build(List<Node> nodes)
{
nodes = new ArrayList<Node>(nodes);
sortList(nodes);
while (nodes.size() > 1)
{
createAndReplace(nodes);
}
return nodes.get(0);
}
/**
*
* 組合兩個權值最小結點,並在結點列表中用它們的父結點替換它們
*
*/
private static void createAndReplace(List<Node> nodes)
{
Node left = nodes.get(0);
Node right = nodes.get(1);
Node parent = new Node(left.getValue() + right.getValue());
parent.setLeftChild(left);
parent.setRightChild(right);
nodes.remove(0);
nodes.remove(0);
nodes.add(parent);
sortList(nodes);
}
/**
*
* 將結點集合由大到小排序
*/
@SuppressWarnings("unchecked")
private static void sortList(List<Node> nodes)
{
Collections.sort(nodes);
}
/**
*
* 打印樹結構,顯示的格式是node(left,right)
*
*/
public static void PrintTree(Node node)
{
Node left = null;
Node right = null;
if (node != null)
{
System.out.print(node.getValue());
left = node.getLeftChild();
right = node.getRightChild();
System.out.println("(" + (left != null ? left.getValue() : " ")
+ "," + (right != null ? right.getValue() : " ") + ")");
}
if (left != null)
PrintTree(left);
if (right != null)
PrintTree(right);
}
}