帶權路徑長度 (WPL) 最小的二叉樹。WPL = Σ(葉子節點權值 × 到根路徑長度)。
從根到葉:左分支編 0,右分支編 1。高頻字符編碼短,低頻字符編碼長,實現無損壓縮。
int[] byteCount = new int[256];
InputStream bis = new BufferedInputStream(new FileInputStream(inputPath));
int b;
while ((b = bis.read()) != -1) {
byteCount[b]++;
}
bis.close();
PriorityQueue pq = new PriorityQueue<>(Comparator.comparingInt(HfmNode::getWeight));
for (int i = 0; i < 256; i++) {
if (byteCount[i] > 0) pq.add(new HfmNode(byteCount[i], i));
}
while (pq.size() > 1) {
HfmNode n1 = pq.poll();
HfmNode n2 = pq.poll();
HfmNode parent = new HfmNode(n1.weight + n2.weight, -1); // -1 表示內部節點
parent.left = n1;
parent.right = n2;
pq.add(parent);
}
HfmNode root = pq.poll();
void buildCodes(HfmNode node, String prefix) {
if (node.isLeaf()) {
codes[node.data] = prefix;
return;
}
if (node.left != null) buildCodes(node.left, prefix + '0');
if (node.right != null) buildCodes(node.right, prefix + '1');
}
for (int i = 0; i < 256; i++) {
bos.write(codes[i] == null ? 0 : codes[i].length());
}
將所有字節的編碼拼成長位串,每 8 位切一字節寫入;最後不足 8 位補 0,並記錄補位數(1 字節)。
讀取原文件每字節,查表輸出對應位串,同樣 8 位打包寫入;結尾補 0 並記錄補位數。
int 累積位,滿 8 位輸出字節;解碼時逐位遍歷哈夫曼樹(0 左 1 右)至葉子輸出。完整源碼包含 HfmNode、HfmTree、Compressor、Decompressor 四大類,可作為教學級壓縮工具參考實現。