【Java】实现哈夫曼树

匿名 (未验证) 提交于 2019-12-02 21:35:18

哈夫曼树

一种二叉树结构,又称最优树,是带权路径最小的树,通常用于哈夫曼编码中。
压缩的思路是统计出字符的使用频率并加以记录,
根据频率从高到低赋予它们从短到长的编码,以此来提高无损压缩率。
下面我们介绍一下哈夫曼树中的基本概念,然后用一个英语文本来构建哈夫曼树。


基本概念

节点

  • 父节点与子节点:它们是相对的概念,一个父节点会有两个子节点,父节点的权为两个子节点之和。
  • 左节点与右节点:根据两个子节点的位置命名。
  • 根节点:最上层的节点,有且只有一个。
  • 叶子节点:最下层的节点,原始数据。

哈夫曼编码

  • 从根节点向下计算,左节点为0,右节点为1。

路径

  • 路径:每个节点之间的通路称之为路径。
  • 路径长度: 路径中分支的数目(生成两个节点为一次)称为路径长度,
  • 从根节点算第一层到第L层之间的路径长度就是L-1,如上图路径长度为2。

Ȩ

(一般节点分为存储域及权及哈夫曼编码,储存域用于存储数据)

  • 结点的权:一个有着某种含义的数值(比如出现频率),则这个数值称为该结点的权。
  • 带权路径长度为:从根结点到该结点之间的路径长度与该结点的权的乘积。
  • 树的带权路径长度:所有叶子结点的带权路径长度之和,记为WPL。
  • (被称为最优树的原因:按照该方法构建的二叉树WPL最小)

构建方法

  • *提前准备: 我们需要一组按序排列的数(如:5,9,12,32,45,55)
  • *构建顺序: 从下到上,即从根节点开始向上构造。
  • 第一步:选择两个最小的数作为子节点(5,9),并构建父节点(14)。
  • 第二步:新的父节点作为新的一个数加入数组中(12,14,32,45,55),再选择两个最小的数进行构建(12,14)。
  • 不断重复,最后结果如图。

    (图中的根节点分别为权值为5,9,12,32,45,55的节点,即数组中原有的数)
    (权值为12的哈夫曼编码为0001)
    (权值为5到158的节点的路径长度为4)
    (权值为26的节点的带权路径长度为2*26=52)
    (每个节点的带权路径长度之和即为这颗树的WPL)

利用英语文本实现哈夫曼树

文本的数据整理

整理目的

  • 获得文本总字数。
  • 获得每个英文字母的出现频率。
  • 将字母及出现频率储存到节点中,并将出现频率为0的剔除掉。
  • 将节点按字母出现频率从高到低进行排列(你可以到最开始看看哈夫曼编码压缩的思路),
  • 并将它们赋予从小到大的权(注意与频率是相反的!)

整理过程

处理思路

这里我们用了比较取巧的方法,没用到map。

因为

  • 因为是简单实现,所以用英文进行处理(算上ASCII码共255个)。
  • 由0到255强制转型为char类型正好对应所有字符。
    所以
  • 创建一个大小为256整型数组,下表对应字符,而数组中储存出现频率。
int [] array=new int[256]; 

字符串的处理

  • 获取字符串长度,对每一个字符进行遍历,并进行如下操作:
for(int i=0;i<string.length;i++) { 	//charat为获取对应位置字符的方法! 	array[(int)string.charAt(i)]++; } 

这样你就得到每个字符的内容以及它对应的出现次数啦!

文件的处理(以txt为例)

  • 这里我们用到的是文件的输入输出流
	int []array=new int[256];	 		try { 			//name处填入地址字符串,如"E:\\【Etaarusa】\\Java\\Train fight\\Data Structer\\MyhuffmanTree\\text.txt" 			FileInputStream In=new FileInputStream(name) ; 			//调用方法获得长度,因为下面的操作会让数值变动所以要先储存。 			int length=In.available(); 			 			for(int i=0;i<length;i++) 			{ 			//按序获得每一个字符,操作与上面的字符串是类似的。 				char a=(char)In.read(); 				System.out.print(a); 				array[(int)a]++; 				array[(int)a]=a; 			} 		} catch (Exception e) { 			// TODO Auto-generated catch block 			e.printStackTrace(); 		} 		 

将数组数据存储进节点中

处理思路

考虑到要将出现次数为0的字符剔除掉
同时获得有效字符数以建立节点数组,
建立完节点以后我们还要将它们按出现次数从小到大的顺序进行排列

处理代码

public static int Stringnum(int[] textarray) 	{ 		int num=0; 		for(int i=0;i<textarray.length;i++) 		{ 			if(textarray[i]!=0)num++; 		} 		System.out.println("有效字符种类数:"+num); 		return num; 	} 	//将数据存储入节点; 	public static MyArrayList<Node> storeData(int num,int[] textarray) 	{ 		MyArrayList<Node> Nodearray=new MyArrayList<Node>(num); 		 		for(int i=0;i<textarray.length;i++) 		{ 			if(textarray[i]!=0) 			{ 				Node a=new Node(); 				a.name=(char)i; 				a.time=textarray[i]; 				Nodearray.add(a); 			} 		} 		//将节点根据出现次数按小到大进行排序; 		 		for(int i=num-1;i>=0;i--) 		{ 			int max=0; 			int place=0; 			for(int j=i;j>=0;j--) 				{ 					if(Nodearray.get(j).time>max) 					{ 						max=Nodearray.get(j).time; 						place=j; 					} 				} 			Node a=Nodearray.get(i); 			Nodearray.set(i,Nodearray.get(place)); 			Nodearray.set(place,a); 		} 		 		return Nodearray; 	} 

用节点构建哈夫曼树

构建思路已在上文,这里我们列出代码:

//按照顺序构造哈夫曼树 	public MyhuffmanTree(MyArrayList<Node> Nodearray) { 		 		size=Nodearray.size; 		 		while(Nodearray.size>1) 		{ 			Node a=Nodearray.remove(0); 			Node b=Nodearray.remove(0); 			a.toMyHString(); 			b.toMyHString(); 			Node fatherNode=new Node(a,b); 			Nodearray.add(fatherNode); 			size++; 		} 		if(Nodearray.size==1) 		{ 			RootNode=Nodearray.get(0); 			RootNode.toMyHString(); 			System.out.println("已得到根节点!"); 		} 	} 

需要注意的是,我这里用到了自己构建的动态数组,
是模仿JDK的arraylist构建的,所以用arraylist是相似效果的。


对哈夫曼树的叶子节点进行哈夫曼编码

编码思路

首先回顾一下哈夫曼编码是左0右1,我们用String来存储哈夫曼编码。
这里我们要用到递归的思想保证能够获取每一个叶子节点。
假设调用的方法称为f,

  • 判断是否有左节点,如果有则该节点的左节点的哈夫曼编码+0后调用f,
  • 判断是否有右节点,如果有则该节点的右节点的哈夫曼编码+1后调用f,
  • 如果都没有则该节点为叶子节点,显示该节点的字符,出现次数,哈夫曼编码。

实现代码

//通过迭代的方法给每个节点进行哈夫曼编码,并将它们进行输出; 	public void huffmanCode(Node n) 	{ 		 		if(n.LeftNode!=null) 		{ 			n.LeftNode.huffmannum=n.huffmannum+0; 			huffmanCode(n.LeftNode); 		} 		if(n.RightNode!=null) 		{ 			n.RightNode.huffmannum=n.huffmannum+1; 			huffmanCode(n.RightNode); 		} 		if(n.LeftNode==null&&n.RightNode==null) 		{ 			n.toMyHString(); 		} 	} 

这样便完成了编码。


2019.2.23
1.哈夫曼树的构造以及哈夫曼编码。
2.哈夫曼压缩还未完成。


文本如下(一篇生化危机2重制的评测)

Last week, I discussed laws relating to quarantine and isolation and considered how public health officials could use those laws to protect the United States from a zombie outbreak. This week, I will pick up where I left off last time and consider some of the interesting issues that could arise if we fail to prevent the zombie disease from spreading.

One of the biggest legal questions implicated by a zombie attack relates to the legal status of zombies and the extent to which zombies would be protected by normal laws. Before beginning the inquiry itself, it is worth reiterating a point I made a few weeks ago. Since we have never experienced an actual zombie outbreak, these scenarios are literally unprecedented. As a result, there is no “right” answer. Instead, the best we can hope to do is draw parallels to circumstances we have seen before and consider whether those parallels are close enough to provide a “best guess” as to what would or should happen.

The question at hand is whether it would be legally permissible to kill zombies in order to reduce the spread of the zombie epidemic. To be clear, the question is not whether it would be legally permissible to kill a zombie in self defense. If you can kill non-zombified people in self-defense, then you would certainly be able to kill zombies. The question is whether it would or should be legal for someone to kill zombies as a preventative measure.

Ironically, this fun, fanciful question is actually a sibling of much more controversial questions regarding abortion and the rights of fetuses. That’s because the question of whether it is permissible to kill a zombie turns on the question of whether zombies have a right to life or, alternatively, whether zombies are alive. We can think of this question in two parts: Do zombies have a right to life as zombies, and do we need to consider any residual rights belonging to the pre-zombified person?

The first question is fairly straightforward and has a pretty easy answer. In most zombie stories, zombies are depicted as animalistic, mindless monsters without any of the qualities we normally attribute to people. They do not think, they do not feel, and they lack what many would call a “soul.” It seems fairly safe and not controversial to conclude that these kinds of zombies are not alive, and thus do not have any rights.

The second question is much more difficult than the first. In most stories, we are lead to believe that one’s transformation into a zombie is permanent and that once someone becomes a zombie, that person is gone forever. If that is the case, then the only rights one would have would be the rights (if any) belonging to the zombie form. In most stories, that form would not be entitled to any rights. However, if the transformation is not permanent, or if we are not sure whether it is permanent, then we are dealing with a diseased person who could one day be cured. In that situation, the question should not be framed as whether the zombie has rights, but should instead relate to how we treat people who are physically or mentally ill. That’s a question we already know the answer to. We should do our best to keep them safe, prevent them from hurting themselves or others, and facilitate their recovery.

In reality, the question would probably be much more complicated since there would likely be some ambiguity regarding the permanent nature of zombification. Even today, the well-recognized concept of “brain death” is controversial in many communities and sometimes leads to conflicts (and legal disputes) over whether someone should be disconnected from life support. As a result, it is easy to see that any discussion relating to the permanent nature of a zombie transformation would be incredibly controversial, and would not lend itself to easy answers. The judgment calls that would normally be associated with questions of “brain death” would also be complicated by the zombie threat. The greater the threat, the easier it is to characterize any zombie killing as reasonable self-defense.

Ultimately, I am left without a solid sense of how the issue of zombie rights would be decided in court. What I am confident about is that the outcome would be incredibly controversial. I imagine Zombie Roe v. Wade ― a landmark Supreme Court decision that divides the country between those who believe that the right to life extends to the undead, and those who believe that transforming into a zombie extinguishes one’s rights.

The fact that my imagined zombie debate perfectly mirrors the abortion debate is a function of the fact that both issues implicate the same questions: What does it mean to be alive? Why do people have rights? How do we balance the rights of a not-entirely-human (a fetus or zombie) against fully formed people (the mother or people who might be threatened by the zombie)? How do we go about resolving these questions?

A few weeks ago, I claimed that “fanciful, imaginative stories can have a substantial and meaningful impact on how we view and perceive the law and its underlying foundations.” I can’t imagine a better example of that principle than the close connection between the zombie rights debate and the abortion debate. The fact that Zombie Roe v. Wade does not yet exist means that we can consider the issues and value questions raised by zombie rights without necessarily tripping over the controversial third-rail that often follows abortion discussions. By thinking through the issues in the context of science fiction zombies, we can abstract away the controversy while engaging in thoughtful and relevant discussions.

And, of course, when the zombies eventually arrive ― as we know they will ― we won’t have to waste any time deciding which side to picket.

完整代码如下

package MyhuffmanTree;  import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream;  import Myarraylist.MyArrayList;  public class MyhuffmanTree { 	MyArrayList<Node> LeaveNode; 	Node RootNode; 	//节点个数; 	int size;  	 	 	//按照顺序构造哈夫曼树 	public MyhuffmanTree(MyArrayList<Node> Nodearray) { 		 		size=Nodearray.size; 		 		while(Nodearray.size>1) 		{ 			Node a=Nodearray.remove(0); 			Node b=Nodearray.remove(0); 			a.toMyHString(); 			b.toMyHString(); 			Node fatherNode=new Node(a,b); 			Nodearray.add(fatherNode); 			size++; 		} 		if(Nodearray.size==1) 		{ 			RootNode=Nodearray.get(0); 			RootNode.toMyHString(); 			System.out.println("已得到根节点!"); 		} 	} 	//通过迭代的方法给每个节点进行哈夫曼编码,并将它们进行输出; 	public void huffmanCode(Node n) 	{ 		 		if(n.LeftNode!=null) 		{ 			n.LeftNode.huffmannum=n.huffmannum+0; 			huffmanCode(n.LeftNode); 		} 		if(n.RightNode!=null) 		{ 			n.RightNode.huffmannum=n.huffmannum+1; 			huffmanCode(n.RightNode); 		} 		if(n.LeftNode==null&&n.RightNode==null) 		{ 			n.toMyHString(); 		} 	} 	public void tohuffmanCode(Node n) 	{ 		 		if(n.LeftNode==null) 		{ 			n.toMyHString(); 		} 		else 		{ 			huffmanCode(n.LeftNode); 		} 		if(n.RightNode==null) 		{ 			n.toMyHString(); 		} 		else 		{ 			huffmanCode(n.RightNode); 		} 	} 	//用一个256位(AsciI码位数)整数数组储存数据; 	//处理字符串; 	public static int[] processingString(String text) 	{  		// 		// 		//对字符串进行哈夫曼编码 		// 		// 		int [] textarray=new int[256]; 		 		for(int i=0;i<text.length();i++) 		{ 			textarray[(int)text.charAt(i)]++; 		} 		 		for(int i=0;i<textarray.length;i++) 		{ 			if(i%4==0)System.out.println(" "); 			System.out.print("【第"+i+"位:"+(char)i+"   出现个数:"+textarray[i]+"】"); 		} 		 		return textarray; 	} 	//处理文件; 	public static int[] processingFile(String name) 	{ 		// 		// 		// 对TxT文件进行哈夫曼编码 		// 		// 		int []array=new int[256];	 		try { 			FileInputStream In=new FileInputStream(name) ; 			 			int length=In.available(); 			 			for(int i=0;i<length;i++) 			{ 				char a=(char)In.read(); 				System.out.print(a); 				array[(int)a]++; 				array[(int)a]=a; 			} 		} catch (Exception e) { 			// TODO Auto-generated catch block 			e.printStackTrace(); 		} 		 		return array; 	} 	//计算数组中的有效位数; 	public static int Stringnum(int[] textarray) 	{ 		int num=0; 		for(int i=0;i<textarray.length;i++) 		{ 			if(textarray[i]!=0)num++; 		} 		System.out.println("有效字符种类数:"+num); 		return num; 	} 	//将数据存储入节点; 	public static MyArrayList<Node> storeData(int num,int[] textarray) 	{ 		MyArrayList<Node> Nodearray=new MyArrayList<Node>(num); 		 		for(int i=0;i<textarray.length;i++) 		{ 			if(textarray[i]!=0) 			{ 				Node a=new Node(); 				a.name=(char)i; 				a.time=textarray[i]; 				Nodearray.add(a); 			} 		} 		//将节点根据出现次数按小到大进行排序; 		 		for(int i=num-1;i>=0;i--) 		{ 			int max=0; 			int place=0; 			for(int j=i;j>=0;j--) 				{ 					if(Nodearray.get(j).time>max) 					{ 						max=Nodearray.get(j).time; 						place=j; 					} 				} 			Node a=Nodearray.get(i); 			Nodearray.set(i,Nodearray.get(place)); 			Nodearray.set(place,a); 		} 		 		return Nodearray; 	} 	public static void toData(int num,MyArrayList<Node> Nodearray) 	{ 		for(int i=0;i<num;i++) 		{ 			if(i%4==0)System.out.println(" "); 			Nodearray.get(i).toMyString(); 		} 	} 	public static void main(String [] args) 	{ 		 		 			 			int [] textarray1=processingString("Last week, I discussed laws relating to quarantine and isolation and considered how public health officials could use those laws to protect the United States from a zombie outbreak. This week, I will pick up where I left off last time and consider some of the interesting issues that could arise if we fail to prevent the zombie disease from spreading."); 			int num1=Stringnum(textarray1); 			MyArrayList<Node> Nodearray1=storeData(num1, textarray1); 			MyhuffmanTree mt1=new MyhuffmanTree(Nodearray1); 			mt1.huffmanCode(mt1.RootNode); 			 			int [] textarray2=processingFile("E:\\【Etaarusa】\\Java\\Train fight\\Data Structer\\MyhuffmanTree\\text.txt"); 			int num2=Stringnum(textarray2); 			MyArrayList<Node> Nodearray2=storeData(num2, textarray2); 			MyhuffmanTree mt2=new MyhuffmanTree(Nodearray2); 			mt2.huffmanCode(mt2.RootNode); 			 			 		 	} 	 }  

然后是Node类:

package MyhuffmanTree;  public class Node { 	char name=' '; 	int  time=0; 	String huffmannum=""; 	Node LeftNode=null; 	Node RightNode=null; 	 	public Node() 	{} 	 	public Node(Node leftNode, Node rightNode) { 		this.LeftNode = leftNode; 		this.RightNode = rightNode; 		time=leftNode.time+rightNode.time; 	}  	public void toMyString() 	{ 		System.out.print(name+": "+time+"  "); 	} 	 	public void toMyHString() 	{ 		System.out.println(name+": "+time+"  "+huffmannum); 	} }  

运算结果太长这里就不放出来了:)

文章来源: https://blog.csdn.net/weixin_44598660/article/details/87880935
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!