学习日志---哈夫曼树相关算法

哈夫曼树概念:

    如果二叉树中的叶结点都带有权值,我们可以把这个定义加以推广。设二叉树有n个带权值的叶结点,定义从二叉树的根结点到二叉树中所有叶结点的路径长度与相应叶结点权值的乘积之和为该二叉树的带权路径长度(WPL),即:

    WPL = 求和(w*li)

    wi为第i个叶结点的权值,li为从根结点到第i个叶结点的路径长度。 

WPL最小的树称为哈夫曼树。

哈夫曼树的结点类

//哈夫曼树的结点类
public class HaffNode {
        //权值一般值出现的权重,越大则表示出现的越多
	int weight; //权值
	int parent ;//双亲
	int flag ;//标志
	int leftChild; //左孩子
	int rightChild;//右孩子
	
	HaffNode()
	{
		
	}
	
}

哈夫曼编码类

//哈夫曼编码类
public class Code {

        //存对应该权值字符的编码
	int[] bit;  //编码数组
	//每个字符的编码长度是一样的,但开始的位置不同,这里的start指的是从哪里开始。
	int start;  //编码的开始下标
	int weight; //权值
	
	Code(int n)
	{
		bit = new int[n];
		start = n-1;
	}
}

哈夫曼树类

哈夫曼树的所有节点都是放在一个数组中。

//哈夫曼树类
public class HaffmanTree {
    //最大权值
	static final int MAXVALUE=1000;
	int nodeNum ; //叶子结点个数
	
	public HaffmanTree(int n)
	{
	   //这里传入有多少个叶子节点
	   this.nodeNum = n;
	}
	
	//构造哈夫曼树算法
	public void haffman(int[] weight,HaffNode[] nodes)
	{
		int n = this.nodeNum;
		//m1,m2,表示最小的两个权值,x1,x2,表示最小两个权值对应的编号
		int m1,m2,x1,x2; 
		
		//初始化所有的结点,对应有n个叶子结点的哈夫曼树,有2n-1个结点。
		for(int i=0;i < 2*n-1;i++)
		{
			HaffNode temp = new HaffNode();
			
	                //初始化n个叶子结点
			if(i < n)
			{
			   temp.weight = weight[i];	
			}
			else
			{
			   temp.weight = 0;	
			}
			temp.parent = 0;
			temp.flag = 0;
			temp.leftChild = -1;
			temp.rightChild = -1;
			nodes[i] = temp;
		}
		
		//初始化n-1个非叶子结点
		for(int i=0;i<n-1;i++)
		{
		   //每次出来后对这俩都要归为最大才能寻找出较小的值
		   m1 = m2 = MAXVALUE;
		   x1 = x2 =0;
		   
	//这个内层的循环是每次加一个节点,然后寻找总的节点中最小的两个没有用过的节点
	//flag的作用是:0为没用过,1是以称为子节点加入树中
	//m1是最小值,m2是次小值
		   for(int j=0;j< n+i;j++)
		   {
			   if(nodes[j].weight<m1 && nodes[j].flag==0)
			   {
				  m2 = m1;
				  x2 = x1;
				  m1 = nodes[j].weight;
				  x1 = j;
			   }
			   else if(nodes[j].weight<m2 && nodes[j].flag ==0)
			   {
				  m2 = nodes[j].weight;
				  x2 = j;
			   }
		   }
		   //每次循环后得到的俩小节点的合节点在n+i位置
		   nodes[x1].parent = n+i;
		   nodes[x2].parent = n+i;
		   nodes[x1].flag = 1;
		   nodes[x2].flag = 1;
		   nodes[n+i].weight = nodes[x1].weight + nodes[x2].weight;
		   nodes[n+i].leftChild = x1;
		   nodes[n+i].rightChild = x2;
		}
		
		
	}
	
	//哈弗曼编码算法
	public void haffmanCode(HaffNode[] nodes,Code[] haffCode)
	{
		int n = this.nodeNum;
		Code code = new Code(n);
		int child,parent;
		
		//对n个叶子节点循环找编码
		for(int i=0;i<n; i++)
		{
		   //反向添加数组
		   code.start = n-1;
		   code.weight = nodes[i].weight;
		   child = i;
		   parent = nodes[child].parent;
		   while(parent!=0)
		   {
			  if(nodes[parent].leftChild == child)
			  {
				  code.bit[code.start] = 0;
			  }
			  else
			  {
				  code.bit[code.start] = 1;
			  }
			  
			  code.start--; 
			  child = parent;
			  parent = nodes[child].parent;
		   }
		   
		   Code temp = new Code(n);
		   //因为code对象数组是来回使用的,因此只能创建一个新的,把指定的位置传入到新的
		   for(int j=code.start+1;j < n;j++)
		   {
			   temp.bit[j] = code.bit[j];
		   }
		   temp.weight = code.weight;
		   temp.start = code.start;
		   haffCode[i] = temp;
		}	
	}	
}

测试类

public class Test {

	public static void main(String[] args) {
		
		int n = 4;
		int[] weight = {1,3,5,7};
		HaffmanTree haffTree = new HaffmanTree(n);
		HaffNode[] nodes = new HaffNode[2*n-1];
		Code[] codes = new Code[n]; 
		//构造哈夫曼树
		haffTree.haffman(weight, nodes);
		//生成哈夫曼编码
		haffTree.haffmanCode(nodes, codes);
		
		//打印哈夫曼编码
		for(int i=0;i<n;i++)
		{
			System.out.print("Weight="+codes[i].weight+" Code=");
			//编码打印时,长度都一样,只是开始的位置不同,由编码类封装了
			for(int j=codes[i].start+1;j<n;j++)
			{
			   System.out.print(codes[i].bit[j]);	
			}
			System.out.println();
		}
	}

}


你可能感兴趣的:(java算法)