您的位置:首页 > 其它

哈夫曼树--顺序结构(建立、编码、解码)

2015-09-15 13:46 483 查看
引子:
	这里的哈夫曼树的建立方法和上一篇不一样,是把数据放在数组上面的,而不是链表上面。因此,采用这种方法建立的哈夫曼树对节点的操作上比链式的哈夫曼树简单很多。对于空间问题,如果有m个item,那么需要建立2*m长度的数组,具体解释请看下面注释!

package tree;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import tree.ListHuffManTree.Node;

public class ListHuffManTreeTest {

	public static void main(String[] args) {
		ListHuffManTree t = new ListHuffManTree(26);
		t.add(4, "A");
		t.add(6, "E");
		t.add(1, "C");
		t.add(8, "D");
		t.add(3, "U");
		t.add(5, "P");
		t.add(7, "B");
		t.add(93, "F");
		t.add(45, "G");
		t.add(34, "H");
		t.add(12, "I");
		t.add(34, "K");
		t.add(22,"O");
		t.add(23, "L");
		//创建哈夫曼树
		t.createHuffmanTree();
		
		t.print();
		//建立哈夫曼编码表
		t.buildCodeTable();
		
		t.printCode();
		//编码字符串hellogoogle
		List<String> codeList = t.encode("HELLOGOOLE");
		//解码
		List<String> decodeList = t.decode(codeList);
		
		System.out.println();
		System.out.print("解码: ");
		for(String s: decodeList){
			System.out.print(s);
		}
		System.out.println();
	}
}
class ListHuffManTree{
	
	private Node[] nodes = null;
	//实际的叶子数
	private int size;
	//最多的叶子数
	private int maxSize;
	//保存编码和源码,其中key为源码
	Map<String,List<String>> codeMap = null;
	
	public ListHuffManTree(int maxSize){
		this.maxSize = maxSize;
		this.size = 0;
		//叶子有maxSize个,则非叶子有maxSize-1个,因为第一个没用,所以有2*maxSize个
		nodes = new Node[2*maxSize];
		
		codeMap = new HashMap<>();
	}
	
	public boolean isFull(){
		return size == maxSize; 
	}
	
	public void add(int weight, String item){
		size++;
		nodes[size] = new Node(weight, item);
	}
	
	/**
	 * 选择权重最小的两个节点
	 * @return
	 */
	private List<Integer> select(){
		List<Integer> list = new ArrayList<Integer>();
		
		int index1 = -1;
		int index2 = -1;
		
		int temp = Integer.MAX_VALUE;
		int i = 1;
		for(; i <= size; i++){
			if(nodes[i].parent == 0 && temp > nodes[i].weight){
				temp = nodes[i].weight;
				index1 = i;
			}
		}
		if(temp != Integer.MAX_VALUE){
			nodes[index1].parent = -1;
			list.add(index1);
		}
		
		temp = Integer.MAX_VALUE;
		int k = 1;
		for(; k <= size; k++){
			if(nodes[k].parent == 0 && temp > nodes[k].weight){
				temp = nodes[k].weight;
				index2 = k;
			}
		}
		if(temp != Integer.MAX_VALUE){
			nodes[index2].parent = -1;
			list.add(index2);
		}
		return list;
	}
	
	/**
	 * 建立哈夫曼树
	 */
	public void createHuffmanTree(){
		int tempSize = this.size;
		for(int i = 0; i < tempSize; i++){
			//选择权值最小的两个节点
			List<Integer> list = select();
			if(list.size() == 2){
				int index1 = list.get(0);
				int index2 = list.get(1);
				
				int w1 = nodes[index1].weight;
				int w2 = nodes[index2].weight;
				
				//合并节点的权重
				add(w1+w2, null);
				nodes[size].lchild = index1;
				nodes[size].rchild = index2;
				
				//改变父节点的index
				nodes[index1].parent = size;
				nodes[index2].parent = size;
			}
		}
	}
	
	/**
	 * 打印节点的详细信息
	 */
	public void print(){
		Node node = null;
		for(int i = 1; i <= this.size; i++){
			node = nodes[i];
			System.out.println("item:"+node.item+" ;weight:"+node.weight+" ;parent:"+node.parent+" ;lchild:"+node.lchild+" ;rchild:"+node.rchild);
		}
	}
	
	/**
	 * 建立编码表
	 * 思路:从每一个叶子节点依次到根节点,是父节点的左孩子,则为0,否则为1
	 */
	public void buildCodeTable(){
		for(int i = 1; i <= size; i++){
			Node node = nodes[i];
			//是否为叶子节点
			if(node.lchild == 0 || node.rchild == 0){
				//用来存储01信息,顺序和实际的相反
				List<String> list = new ArrayList<>();
				
				String tempKey = nodes[i].item;
				
				int tempInt = i;
				while(node.parent > 0){
					int p = node.parent;
					if(tempInt == nodes[p].lchild){
						list.add("0");
					}else{
						list.add("1");
					}
					node = nodes[p];
					tempInt = p;
				}
				codeMap.put(tempKey, list);
			}
		}
	}
	
	/**
	 * 打印二进制编码
	 */
	public void printCode(){
		Set<Entry<String,List<String>>> set = codeMap.entrySet();
		Iterator<Entry<String,List<String>>> iter = set.iterator();
		while(iter.hasNext()){
			Entry<String,List<String>> entry = iter.next();
			System.out.print(entry.getKey()+": ");
			List<String> list = entry.getValue();
			for(int i = list.size()-1; i >= 0; i--){
				System.out.print(list.get(i));
			}
			System.out.println();
		}
	}
	
	public List<String> encode(String target){
		System.out.print(target+" 的编码: ");
		
		List<String> li = new ArrayList<String>();
		
		int len = target.length();
		for(int i = 0; i < len; i++){
			String temp = String.valueOf(target.charAt(i));
			List<String> list = codeMap.get(temp);
			for(int k = list.size()-1; k >= 0; k--){
				System.out.print(list.get(k));
				li.add(list.get(k));
			}
		}
		return li;
	}
	
	/**
	 * 解码
	 * @param codeList 二进制编码
	 * @return 解码结果
	 */
	public List<String> decode(List<String> codeList){
		
		List<String> list = new ArrayList<String>();
		
		int tempSize = size;
		for(String s : codeList){
			if("0".equals(s)){
				int lch = nodes[tempSize].lchild;
				String itemString = nodes[lch].item;
				if(itemString != null){
					list.add(itemString);
					tempSize = size;
					continue;
				}
				tempSize = lch;
			}else if("1".equals(s)){
				int rch = nodes[tempSize].rchild;
				String itemString = nodes[rch].item;
				if(itemString != null){
					list.add(itemString);
					tempSize = size;
					continue;
				}
				tempSize = rch;
			}
		}
		return list;
	}
	
	/**
	 * 节点
	 * @author LiangYH
	 *
	 */
	class Node{
		//parent index
		int parent;
		//left child index
		int lchild;
		//right child index
		int rchild;
		//rate
		int weight;
		//
		String item;
		
		public Node(){
			this.item = null;
			this.lchild = 0;
			this.rchild = 0;
			this.parent = 0;
			this.weight = -1;
		}
		
		public Node(int weight, String item){
			this.weight = weight;
			this.item = item;
			this.parent = 0;
			this.lchild = 0;
			this.rchild = 0;
		}
	}
}
结果:


思考:上面选择权值最小的两个节点的方法不是特别理想,如何改进?
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: