Java实现特殊二叉树之堆、哈夫曼树


什么是:堆是一种经过排序的完全二叉树,其中任一非终端节点的数据值均不大于(或不小于)其左孩子和右孩子节点的值;

       首先,堆分为大根堆(最大堆)和小根堆(最小堆)二种;

       小根堆满足:

               (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);

	}

}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章