首先,我們需要了解一下我們平時的文件是如何保存的。不難理解;不管是什么類型的文件都是以字節的形式存儲在我們的各種儲存器中的,以二進制的方式將數據儲存起來。而我們需要找到一種能夠占用內存更少的方式將我們的數據儲存。下面我將以壓縮字符串為例仔細探討如何利用哈夫曼二叉樹(最優二叉樹)壓縮文件。
首先需要一個字符串,String str=“QQAFDGGFDAAGFGFDHGFHG”;然后我們需要對每一個字符進行哈夫曼編碼,即找到每一個出現的字符找個屬於它的哈夫曼編碼,當然這不是隨便亂給的。下面來看一下如何給這個字符串編碼。
①統計字符串中每個字符出現的次數,例中:A:3 D:3 F:5 G:6 H:2 Q:2
關鍵代碼:
int []temp=new int[256]; public void statisticsString(String str){ byte b[]=str.getBytes(); for(int i=0;i<b.length;i++){ temp[b[i]+128]++; } }
②將每一個字符出現的次數作為該字符的一個權值,A的權值為3、D為3、F為5、G為6、H為2、Q為2.然后按權值大小將字符排隊,H(2)、Q(2)、A(3)、D(3)、F(5)、G(6)
③最最關鍵的一步就是生成樹,應該怎么生成一顆哈夫曼二叉樹呢?1、首先我們找出排好順序的字符,每一個字符作為一個子結點,(這時候我們需要為我們的結點寫一個結點類(文章末尾有我自己寫的Node類)了,結點類中需要有兩個指針,和兩個數據)。每一個字符生成一個結點對象,保存兩個數據,一個是字符,一個是字符的權值,將所有的結點保存在一個鏈表中。2、取出權值最小的兩個字符作為葉結點,生成一個根結點,根結點也保存兩個數據,一個是生成它的兩個葉結點的字符(兩個字符相連接在一起保存),一個是生成它的兩個葉結點的權值和3、將新的結點加入鏈表中,刪除兩個權值最小的結點,然后按照權值大小重新排序。4、然后重復2、3步直到剩下一個結點為止。由下圖所示:
關鍵代碼:
public void spanning(){ list=new ArrayList<Node>(); int count=0; for(int i=0;i<temp.length;i++){ if(temp[i]!=0){ Node<String> node=new Node<String>(""+(i-128),temp[i]); list.add(node); count++; } } int data[]=new int[count]; for(int i=0;i<count;i++){ data[i]=(int) (list.get(i).data1); } Arrays.sort(data); for(int i=0;i<data.length;i++){ for(int j=0;j<count;j++){ if(list.get(j).data1==data[i]){ list.add(list.get(j)); list.remove(j); count--; break; } } } while(list.size()>1){ n=new Node((String)list.get(0).data+list.get(1).data,list.get(0).data1+list.get(1).data1,list.get(0),list.get(1)); list.remove(0); list.remove(0); for(int i=0;i<=list.size();i++){ if(list.size()==0){list.add(n);break;} else if(i==list.size()){list.add(n);break;} else if(n.data1<=list.get(i).data1){ list.add(i, n); break; } } } }
④然后給生成的樹編碼,左零右一。
關鍵代碼:
map=new HashMap<String, String>(); public void ergodic(Node node,String code){ if(node==null)return ; if(node.next!=null){ ergodic(node.next,code+"1"); } if(node.next2!=null){ ergodic(node.next2,code+"0"); } if(node.next==null&&node.next2==null){ map.put(""+node.data,code); } }
⑤按着路徑找到每個葉結點的編碼,H:010 Q:011 A:100 D:101 F:00 G:11
⑥將編碼代替字符串。例中:“QQAFDGGFDAAGFGFDHGFHG”替換成 :
0110111000010111110010110010011001100101010110001011然后我們就將我們的字符串替換成了二進制數串。
關鍵代碼:
public String read(String str){ String code=""; byte[] b=str.getBytes(); for(int i=0;i<b.length;i++){ code+=map.get(""+b[i]); } return code; }
⑦將我們的二進制數串每八個換成一個十進制數字,最后不足八個就補0,例中的就替換成:
217-200-36-217-18-229-192
關鍵代碼:
public String compressFile(String path,String fileName,String code){ int coding; String sa=""; byte co[]=new byte[code.length()/8+1]; for(int i=0,j=0;i<code.length()-code.length()%8;i+=8,j++){ String s=code.substring(i,i+8); coding=Integer.parseInt(s, 2); co[j]=(byte)coding; sa+=""+coding+"-"; } if(code.length()%8!=0){ String s=code.substring(code.length()-code.length()%8,code.length()); for(int i=0;i<8-code.length()%8;i++)s+="0"; coding=Integer.parseInt(s, 2); co[code.length()/8]=(byte)coding; sa+=""+coding; } System.out.println(sa); return sa; }
⑧然后將我們得到的十進制數保存成文件,這樣我們就實現了我們的字符串的壓縮,而如何壓縮文件呢?同樣道理,一個文件是有一個一個字節組成的,我們同樣可以統計每個字符出現的次數,我們只需要按照上面的流程走一遍就可以了。
public class Node<E> { E data; int data1; Node<E> next; Node<E> next2; MyLinkList<E> mylist; public Node(){ } public Node(E e1,int e,Node<E> next,Node<E> next2){ data=e1; data1=e; this.next=next; this.next2=next2; } public Node(E e,int e1){ data=e; data1=e1; } public Node(E e){ data=e; } public Node(E e,Node<E> next){ data=e;2016-08-13 this.next=next; } }