1+ package algorithms ;
2+
3+ import java .io .*;
4+ import java .util .*;
5+
6+ //очередь с приоритетами
7+ class PriorQueue <T extends Comparable > {
8+ //на основе обычного ссылочного массива
9+ ArrayList <T > arr = new ArrayList <T >();
10+
11+ //добавляем в конец = O(1)
12+ public void add (T obj ) {
13+ arr .add (obj );
14+ }// add
15+
16+ //извлекаем наименьший элемент = O(N)
17+ public T get () {
18+ if (arr .size () == 0 )
19+ return null ;
20+
21+ //ищем наименьший элемент
22+ T prior = arr .get (0 );
23+ for (int i = 1 ; i < arr .size (); i ++) {
24+ if (arr .get (i ).compareTo (prior ) < 0 ) {
25+ prior = arr .get (i );
26+ }
27+ }
28+ //найденный элемент удаляем
29+ arr .remove (prior );
30+
31+ return prior ;
32+ }// add
33+
34+ public int size () {
35+ return arr .size ();
36+ }
37+ } // PriorQueue
38+
39+ public class Huffman {
40+ //вершина дерева - вверху (наименьший путь) популярные записи, внизу редкие
41+ Node root ;
42+
43+ //таблицы соответствия = буква - битовая карта
44+ // [буква] = бинарный код
45+ String codeTable_in [] = new String [256 ];
46+ // [бинарный код] = буква
47+ HashMap <String , Character > codeTable_out = new HashMap <String , Character >();
48+
49+ public Huffman (Node r ) {
50+ this .root = r ;
51+ }
52+
53+ //ветвь дерева
54+ static class Node implements Comparable <Node > {
55+ //буква
56+ public Character ch ;
57+ //частота символа
58+ public Integer freq ;
59+ //ветви ниже
60+ public Node left ;
61+ public Node right ;
62+
63+ public Node (Character c , Integer f ) {
64+ this .ch = c ;
65+ this .freq = f ;
66+ } // Node
67+
68+ public Node (Node l , Node r ) {
69+ //частота родителя = сумме частоты детей
70+ this .freq = l .freq + r .freq ;
71+ this .left = l ;
72+ this .right = r ;
73+ } // Node
74+
75+ @ Override
76+ public int compareTo (Node h ) {
77+ return this .freq - h .freq ;
78+ } // compareTo
79+
80+ public void dump (Huffman parrent ) {
81+ if (this .left != null )
82+ this .left .dump (parrent );
83+ if (this .ch != null ) {
84+ String pout = this .ch + " (" + this .freq + ")" ;
85+ if (parrent != null ) {
86+ pout = pout + " = " + parrent .codeTable_in [(char ) this .ch ];
87+ }
88+ System .out .println (pout );
89+ }
90+ if (this .right != null )
91+ this .right .dump (parrent );
92+ } // dump
93+ } // node
94+
95+ //частота символов в файле
96+ public static int [] getFreqFromFile (String file ) throws IOException {
97+ int [] freq = new int [256 ];
98+ BufferedReader br = new BufferedReader (
99+ new FileReader (file ));
100+ try {
101+ String line = br .readLine ();
102+ while (line != null ) {
103+ for (char c : line .toCharArray ()) {
104+ //если это необходимые символы
105+ if (c > 0 && c < 256 ) {
106+ //увеличиваем счетчик кол-ва символов
107+ freq [c ]++;
108+ }
109+ }
110+ line = br .readLine ();
111+ }
112+ } finally {
113+ br .close ();
114+ }
115+ return freq ;
116+ } // getFreqFromFile
117+
118+ //создаем таблицу соответствия буква - сжатая битовая карта
119+ public void makeCodeTable () {
120+ //прямая карта
121+ makeCodeTableIn (this .root , "" );
122+ //обратая карта
123+ for (int i = 0 ; i < this .codeTable_in .length ; i ++) {
124+ codeTable_out .put (this .codeTable_in [(char ) i ], (char ) i );
125+ }
126+ } // makeCodeTableIn
127+
128+ protected void makeCodeTableIn (Node n , String code ) {
129+ if (n == null )
130+ return ;
131+ if (n .ch != null ) {
132+ this .codeTable_in [(char ) n .ch ] = code ;
133+ }
134+ //при переходе ниже левее увеличиваем битовую карту на 0 справа
135+ this .makeCodeTableIn (n .left , code + "0" );
136+ //при перехода направо ниже увеличиваем битовую карту на 1 справа
137+ this .makeCodeTableIn (n .right , code + "1" );
138+ } // makeCodeTableIn
139+
140+ //сжатие текста
141+ public String compress (String txt ) {
142+ String result = new String ();
143+
144+ //проходимся по каждому символу текста
145+ for (int i = 0 ; i < txt .length (); i ++) {
146+ //если символ есть в таблице преобразования
147+ if (txt .charAt (i ) < 256 && this .codeTable_in [txt .charAt (i )] != null ) {
148+ result = result + this .codeTable_in [txt .charAt (i )];
149+ } else {
150+ //если нет, то просто вставляем символ
151+ result = result + txt .charAt (i );
152+ }
153+ }
154+ return result ;
155+ } // compress
156+
157+ //разжатие текста
158+ public String decompress (String txt ) {
159+ String result = new String ();
160+ String buf = "" ;
161+
162+ //обходим каждый бит
163+ for (int i = 0 ; i < txt .length (); i ++) {
164+ //накапливаем буфер битов
165+ buf = buf + txt .charAt (i );
166+
167+ //если буфер есть в таблице соответствия
168+ if (codeTable_out .containsKey (buf )) {
169+ //берем из таблицы и сбрасываем буфер
170+ result = result + codeTable_out .get (buf );
171+ buf = "" ;
172+ } else if (txt .charAt (i ) >= 256 || this .codeTable_in [txt .charAt (i )] == null ) {
173+ // нет в таблице преобразования - выдаем как есть
174+ result = result + txt .charAt (i );
175+ buf = "" ;
176+ }
177+ }
178+ result = result + buf ;
179+ return result ;
180+ } // compress
181+
182+ public void dump () {
183+ this .root .dump (this );
184+ } // dump
185+
186+ public static void main (String [] args ) throws IOException {
187+ int [] freq = getFreqFromFile ("C:\\ Users\\ 007\\ Google Диск\\ info\\ blog-02-17-2017.xml" );
188+ PriorQueue <Node > pq = new PriorQueue ();
189+ for (int i = 0 ; i < freq .length ; i ++) {
190+ if (freq [i ] > 0 ) {
191+ pq .add (new Node ((char ) i , freq [i ]));
192+ }
193+ }
194+ while (pq .size () > 1 ) {
195+ Node l = pq .get ();
196+ Node r = pq .get ();
197+ pq .add (new Node (l , r ));
198+ }
199+
200+ Huffman h = new Huffman (pq .get ());
201+ h .makeCodeTable ();
202+ String test_string = "abcdeя f`d" ;
203+ String compr_string = h .compress (test_string );
204+ String decompr_string = h .decompress (compr_string );
205+ System .out .println (test_string );
206+ System .out .println (compr_string );
207+ System .out .println (decompr_string );
208+ // h.dump();
209+ } // main
210+ } // Huffman
0 commit comments