Huffman Coding เป็นอัลกอริทึมบีบอัดข้อมูลแบบ Lossless (ไม่สูญเสียข้อมูล) ใช้รหัสความยาวไม่เท่ากัน (Variable-length codes) โดยตัวอักษรที่พบบ่อยได้รหัสสั้น ตัวอักษรที่พบน้อยได้รหัสยาว
หลักการทำงาน
- นับความถี่ (Frequency) ของแต่ละตัวอักษร
- สร้าง Huffman Tree โดยใช้ Priority Queue (Min-Heap)
- เดินทาง (Traverse) ต้นไม้: ซ้าย = 0, ขวา = 1
- รหัสของแต่ละตัวอักษร = เส้นทางจากรูทถึงใบ
ตัวอย่าง
Input: "ACCEBFFFFAAXXBLKE" Frequency: A:3, B:2, C:2, E:2, F:4, K:1, L:1, X:2 Codes: F: 01 (บ่อยที่สุด -> สั้นที่สุด) A: 111 B: 100 C: 101 E: 001 X: 110 K: 0000 L: 0001 (น้อยที่สุด -> ยาวที่สุด)
ความซับซ้อน (Complexity)
- Time: O(n log n) - เนื่องจาก Priority Queue operations
- Space: O(n) - เก็บต้นไม้และรหัส
อัลกอริทึม (Pseudocode)
huffmanCoding(string)
freq[256] = {0}
for each char c in string:
freq[c]++
PriorityQueue Q (min-heap by freq)
for each char with freq > 0:
Q.push(Node(char, freq))
while Q.size() > 1:
left = Q.pop()
right = Q.pop()
parent = Node('\0', left.freq + right.freq)
parent.left = left; parent.right = right
Q.push(parent)
root = Q.pop()
traverse(root, "")
traverse(node, code)
if node.left:
traverse(node.left, code + "0")
traverse(node.right, code + "1")
else:
print node.char + ": " + code
ตัวอย่างโค้ด C++
#include <iostream>
#include <queue>
#include <string>
#include <unordered_map>
using namespace std;
struct Node {
char data;
int freq;
Node *left, *right;
Node(char d, int f) : data(d), freq(f), left(nullptr), right(nullptr) {}
Node(Node* l, Node* r) : data('\0'), freq(l->freq + r->freq), left(l), right(r) {}
bool operator<(const Node& other) const { return freq > other.freq; } // min-heap
};
void traverse(Node* node, string code) {
if (node->left) {
traverse(node->left, code + "0");
traverse(node->right, code + "1");
} else {
cout << "Data: " << node->data << ", Freq: " << node->freq << ", Code: " << code << endl;
}
}
int main() {
string str = "ACCEBFFFFAAXXBLKE";
int freq[256] = {0};
for (char c : str) freq[(int)c]++;
priority_queue<Node*> pq;
for (int i = 0; i < 256; i++)
if (freq[i] > 0) pq.push(new Node((char)i, freq[i]));
while (pq.size() > 1) {
Node* left = pq.top(); pq.pop();
Node* right = pq.top(); pq.pop();
pq.push(new Node(left, right));
}
cout << "Huffman Codes:\n";
traverse(pq.top(), "");
}
คุณสมบัติสำคัญ
- Prefix-free: ไม่มีรหัสตัวหนึ่งเป็นคำนำหน้าของอีกตัว (Decode ได้อย่างเดียว)
- Optimal: ให้ความยาวรหัสเฉลี่ยน้อยที่สุดสำหรับความถี่ที่กำหนด
- Lossless: ถอดรหัสกลับได้ข้อมูลต้นฉบับสมบูรณ์
การประยุกต์ใช้
- บีบอัดไฟล์: ZIP, GZIP, PNG, MP3, JPEG (ส่วนหนึ่ง)
- โปรโตคอลเครือข่าย, ฟอนต์, ฐานข้อมูล