Алгоритм кодування Хаффмана був запропонований Девід А. Хаффман в 1950 році. Це a стиснення даних без втрат механізм. Він також відомий як кодування стиснення даних. Він широко використовується для стиснення зображень (JPEG або JPG). У цьому розділі ми обговоримо Кодування Хаффмана і розшифровка, а також реалізувати свій алгоритм у програмі на Java.
Ми знаємо, що кожен символ є послідовністю 0 і 1 і зберігається за допомогою 8 біт. Механізм називається кодування фіксованої довжини оскільки кожен символ використовує однакову кількість пам’яті з фіксованими бітами.
символи екранування java
Тут виникає питання, чи можна зменшити обсяг місця, необхідного для зберігання символу?
так, це можливо за допомогою кодування змінної довжини. У цьому механізмі ми використовуємо деякі символи, які зустрічаються частіше порівняно з іншими символами. У цій техніці кодування ми можемо представити той самий фрагмент тексту або рядок, зменшивши кількість бітів.
Кодування Хаффмана
Кодування Хаффмана реалізує наступні кроки.
- Він призначає код змінної довжини всім заданим символам.
- Довжина коду символу залежить від того, як часто він зустрічається в тексті або рядку.
- Символ отримує найменший код, якщо він часто зустрічається.
- Символ отримує найбільший код, якщо він зустрічається найменше.
Кодування Хаффмана відповідає a правило префікса що запобігає неоднозначності під час декодування. Правило також гарантує, що код, призначений символу, не розглядається як префікс коду, призначеного будь-якому іншому символу.
Кодування Хаффмана складається з двох основних етапів:
- Спочатку побудуйте a Дерево Хаффмана із заданого рядка введення, символів чи тексту.
- Призначте код Хаффмана кожному символу, переходячи по дереву.
Давайте коротко розповімо про два вищезазначені кроки.
Дерево Хаффмана
Крок 1: Для кожного символу вузла створіть листовий вузол. Ліцевий вузол символу містить частоту цього символу.
Крок 2: Встановіть усі вузли в порядку їх частоти.
крок 3: Може існувати умова, за якої два вузли можуть мати однакову частоту. У такому випадку виконайте наступне:
- Створіть новий внутрішній вузол.
- Частота вузла буде сумою частот цих двох вузлів, які мають однакову частоту.
- Позначте перший вузол як лівий дочірній елемент, а інший вузол — як правий дочірній елемент новоствореного внутрішнього вузла.
крок 4: Повторюйте кроки 2 і 3, доки всі вузли не сформують одне дерево. Таким чином, ми отримуємо дерево Хаффмана.
Приклад кодування Хаффмана
Припустимо, ми повинні закодувати рядок Абра Кадабра. Визначте наступне:
- Код Хаффмана для всіх персонажів
- Середня довжина коду для даного рядка
- Довжина закодованого рядка
(i) Код Хаффмана для всіх символів
Щоб визначити код для кожного символу, спочатку ми будуємо a Дерево Хаффмана.
Крок 1: Складіть пари символів і їх частоти.
(a, 5), (b, 2), (c, 1), (d, 1), (r, 2)
амріта рао актор
Крок 2: Відсортувавши пари за частотою, отримаємо:
(в, 1), (г, 1), (б, 2) (р, 2), (а, 5)
крок 3: Виберіть перших двох символів і об’єднайте їх під батьківським вузлом.
Ми спостерігаємо, що батьківський вузол не має частоти, тому ми повинні призначити йому частоту. Частота батьківського вузла буде сумою дочірніх вузлів (лівого та правого), тобто 1+1= 2.
рівність об’єктів у java
крок 4: Повторюйте кроки 2 і 3, поки не отримаємо одне дерево.
Ми спостерігаємо, що пари вже відсортовані (за кроком 2). Знову виберіть перші дві пари та об’єднайте їх.
Ми спостерігаємо, що батьківський вузол не має частоти, тому ми повинні призначити йому частоту. Частота батьківського вузла буде сумою дочірніх вузлів (лівого та правого), тобто 2+2= 4.
Знову ми перевіряємо, чи пари відсортовані чи ні. На цьому кроці нам потрібно відсортувати пари.
Згідно з кроком 3, вибираємо перші дві пари та об’єднуємо їх, отримуємо:
Ми спостерігаємо, що батьківський вузол не має частоти, тому ми повинні призначити йому частоту. Частота батьківського вузла буде сумою дочірніх вузлів (лівого та правого), тобто 2+4= 6.
Знову ми перевіряємо, чи пари відсортовані чи ні. На цьому кроці нам потрібно відсортувати пари. Після сортування дерево виглядає так:
Згідно з кроком 3, вибираємо перші дві пари та об’єднуємо їх, отримуємо:
Ми спостерігаємо, що батьківський вузол не має частоти, тому ми повинні призначити йому частоту. Частота батьківського вузла буде сумою дочірніх вузлів (лівого та правого), тобто 5+6= одинадцять.
Таким чином, ми отримуємо єдине дерево.
Нарешті ми знайдемо код для кожного символу за допомогою наведеного вище дерева. Призначте вагу кожному краю. Зверніть увагу, що кожен зважений лівий край дорівнює 0 і зважений правий край дорівнює 1.
Ми спостерігаємо, що вхідні символи представлені лише у вихідних вузлах, а внутрішні вузли мають нульові значення. Щоб знайти код Хаффмана для кожного символу, перейдіть по дереву Хаффмана від кореневого вузла до кінцевого вузла того конкретного символу, для якого ми хочемо знайти код. У таблиці описано код і довжину коду для кожного символу.
характер | Частота | Код | Довжина коду |
---|---|---|---|
А | 5 | 0 | 1 |
Б | 2 | 111 | 3 |
C | 1 | 1100 | 4 |
Д | 1 | 1101 | 4 |
Р | 2 | 10 | 2 |
Ми спостерігаємо, що найчастіший символ отримує найменшу довжину коду, а менш частий символ отримує найбільшу довжину коду.
Тепер ми можемо закодувати рядок (Абра Кадабра) які ми взяли вище.
0 111 10 0 1100 0 1101 0 111 10 0
(ii) Середня довжина коду для рядка
Середню довжину коду дерева Хаффмана можна визначити за допомогою наведеної нижче формули:
Average Code Length = ∑ ( frequency × code length ) / ∑ ( frequency )
= { (5 x 1) + (2 x 3) + (1 x 4) + (1 x 4) + (2 x 2) } / (5+2+1+1+2)
тестування та види програмного забезпечення
= 2,09090909
(iii) Довжина закодованого рядка
Довжину закодованого повідомлення можна визначити за такою формулою:
length= Total number of characters in the text x Average code length per character
= 11 х 2,09090909
= 23 біти
Алгоритм кодування Хаффмана
Huffman (C) n=|C| Q=C for i=1 to n-1 do z=allocate_Node() x=left[z]=Extract_Min(Q) y=right[z]=Extract_Min(Q) f[z]=f[x]+f[y] Insert(Q,z) return Extract_Min(Q)
Алгоритм Хаффмана є жадібним алгоритмом. Оскільки на кожному етапі алгоритм шукає найкращі доступні варіанти.
Часова складність кодування Хаффмана становить O(nlogn). Де n – кількість символів у даному тексті.
Декодування Хаффмана
Декодування Хаффмана - це техніка, яка перетворює закодовані дані у початкові дані. Як ми бачили під час кодування, дерево Хаффмана створюється для вхідного рядка, а символи декодуються на основі їхньої позиції в дереві. Процес декодування виглядає наступним чином:
python записує json у файл
- Почніть рух по дереву з корінь вузол і пошук персонажа.
- Якщо ми рухаємося ліворуч у бінарному дереві, додаємо 0 до коду.
- Якщо ми рухаємося праворуч у двійковому дереві, додайте 1 до коду.
Дочірній вузол містить вхідний символ. Йому присвоюється код, утворений наступними 0 і 1. Часова складність декодування рядка становить O(n), де n - довжина рядка.
Програма Хаффмана для кодування та декодування Java
У наведеній нижче програмі ми використали такі структури даних, як пріоритетні черги, стеки та дерева, щоб розробити логіку стиснення та розпакування. Ми будуватимемо наші утиліти на основі широко використовуваної алгоритмічної техніки кодування Хаффмана.
HuffmanCode.java
import java.util.Comparator; import java.util.HashMap; import java.util.Map; import java.util.PriorityQueue; //defining a class that creates nodes of the tree class Node { //storing character in ch variable of type character Character ch; //storing frequency in freq variable of type int Integer freq; //initially both child (left and right) are null Node left = null; Node right = null; //creating a constructor of the Node class Node(Character ch, Integer freq) { this.ch = ch; this.freq = freq; } //creating a constructor of the Node class public Node(Character ch, Integer freq, Node left, Node right) { this.ch = ch; this.freq = freq; this.left = left; this.right = right; } } //main class public class HuffmanCode { //function to build Huffman tree public static void createHuffmanTree(String text) { //base case: if user does not provides string if (text == null || text.length() == 0) { return; } //count the frequency of appearance of each character and store it in a map //creating an instance of the Map Map freq = new HashMap(); //loop iterates over the string and converts the text into character array for (char c: text.toCharArray()) { //storing character and their frequency into Map by invoking the put() method freq.put(c, freq.getOrDefault(c, 0) + 1); } //create a priority queue that stores current nodes of the Huffman tree. //here a point to note that the highest priority means the lowest frequency PriorityQueue pq = new PriorityQueue(Comparator.comparingInt(l -> l.freq)); //loop iterate over the Map and returns a Set view of the mappings contained in this Map for (var entry: freq.entrySet()) { //creates a leaf node and add it to the queue pq.add(new Node(entry.getKey(), entry.getValue())); } //while loop runs until there is more than one node in the queue while (pq.size() != 1) { //removing the nodes having the highest priority (the lowest frequency) from the queue Node left = pq.poll(); Node right = pq.poll(); //create a new internal node with these two nodes as children and with a frequency equal to the sum of both nodes' frequencies. Add the new node to the priority queue. //sum up the frequency of the nodes (left and right) that we have deleted int sum = left.freq + right.freq; //adding a new internal node (deleted nodes i.e. right and left) to the queue with a frequency that is equal to the sum of both nodes pq.add(new Node(null, sum, left, right)); } //root stores pointer to the root of Huffman Tree Node root = pq.peek(); //trace over the Huffman tree and store the Huffman codes in a map Map huffmanCode = new HashMap(); encodeData(root, '', huffmanCode); //print the Huffman codes for the characters System.out.println('Huffman Codes of the characters are: ' + huffmanCode); //prints the initial data System.out.println('The initial string is: ' + text); //creating an instance of the StringBuilder class StringBuilder sb = new StringBuilder(); //loop iterate over the character array for (char c: text.toCharArray()) { //prints encoded string by getting characters sb.append(huffmanCode.get(c)); } System.out.println('The encoded string is: ' + sb); System.out.print('The decoded string is: '); if (isLeaf(root)) { //special case: For input like a, aa, aaa, etc. while (root.freq-- > 0) { System.out.print(root.ch); } } else { //traverse over the Huffman tree again and this time, decode the encoded string int index = -1; while (index <sb.length() - 1) { index="decodeData(root," index, sb); } traverse the huffman tree and store codes in a map function that encodes data public static void encodedata(node root, string str, huffmancode) if (root="=" null) return; checks node is leaf or not (isleaf(root)) huffmancode.put(root.ch, str.length()> 0 ? str : '1'); } encodeData(root.left, str + '0', huffmanCode); encodeData(root.right, str + '1', huffmanCode); } //traverse the Huffman Tree and decode the encoded string function that decodes the encoded data public static int decodeData(Node root, int index, StringBuilder sb) { //checks if the root node is null or not if (root == null) { return index; } //checks if the node is a leaf node or not if (isLeaf(root)) { System.out.print(root.ch); return index; } index++; root = (sb.charAt(index) == '0') ? root.left : root.right; index = decodeData(root, index, sb); return index; } //function to check if the Huffman Tree contains a single node public static boolean isLeaf(Node root) { //returns true if both conditions return ture return root.left == null && root.right == null; } //driver code public static void main(String args[]) { String text = 'javatpoint'; //function calling createHuffmanTree(text); } } </sb.length()>
Вихід:
Huffman Codes of the characters are: {p=000, a=110, t=111, v=001, i=010, j=011, n=100, o=101} The initial string is: javatpoint The encoded string is: 011110001110111000101010100111 The decoded string is: javatpoint