哈夫曼编码
【理论知识可以参考】
# 哈夫曼编解码过程要依赖一棵最优二叉树,ta具有最小带权路径长度wpl,所以首先需要生成这个最优二叉树
# 这棵最优二叉树也叫哈夫曼树
1 local HuffmanTreeNode = {} 2 HuffmanTreeNode.__index = HuffmanTreeNode 3 4 function HuffmanTreeNode.new(w, v) 5 local obj = {} 6 setmetatable(obj, HuffmanTreeNode) 7 obj:ctor(w, v) 8 return obj 9 end 10 11 function HuffmanTreeNode:ctor(w, v) 12 self.weight = w 13 self.value = v or w---元素值 14 self.left = nil ---左子节点 15 self.right = nil ---右子节点 16 self.parent = nil ---父节点 17 end 18 19 function HuffmanTreeNode:SetLeft(l) 20 self.left = l 21 l.parent = self 22 end 23 24 function HuffmanTreeNode:SetRight(r) 25 self.right = r 26 r.parent = self 27 end 28 29 function HuffmanTreeNode:IsLeaf() 30 return nil == self.left and nil == self.right 31 end
# 构建的过程就是不断的取2个权值最小的节点组成新节点,直到取完
# 如果遇到新组成的节点权值和已有节点相同的,优先用已有节点
1 local HuffmanTree = {} 2 HuffmanTree.__index = HuffmanTree 3 4 function HuffmanTree.new() 5 local obj = {} 6 setmetatable(obj, HuffmanTree) 7 obj:ctor() 8 return obj 9 end 10 11 function HuffmanTree:ctor() 12 self.root = nil ---根节点 13 self.codesQueryDict = nil ---编码查询表 14 end 15 16 function HuffmanTree:Build(nodeArr) 17 local ct = #nodeArr 18 19 local minHeap = {} --这边暂时用数组模拟最小堆 20 for i=1,ct do 21 minHeap[i] = nodeArr[i] 22 end 23 local sortFunc = function(a, b) 24 if a.weight == b.weight then 25 local aNoParent = (nil == a.parent) 26 local bNoParent = (nil == b.parent) 27 if aNoParent ~= bNoParent then 28 return aNoParent 29 end 30 end 31 return a.weight < b.weight --小的排前面 32 end 33 table.sort(minHeap, sortFunc) 34 35 local parent = nil 36 while #minHeap > 1 do 37 local node1 = minHeap[1] 38 local node2 = minHeap[2] 39 table.remove(minHeap, 1) 40 table.remove(minHeap, 1) 41 42 parent = HuffmanTreeNode.new(node1.weight + node2.weight, nil) 43 parent:SetLeft(node1) 44 parent:SetRight(node2) 45 46 table.insert(minHeap, parent) 47 table.sort(minHeap, sortFunc) 48 end 49 50 self.root = parent 51 end 52 53 function HuffmanTree:__tostring() 54 if nil == self.root then return end 55 56 local levelStr = {} 57 local function preOrder(level, node) 58 if nil == node then return end 59 60 local str = levelStr[level] 61 if nil == str then 62 str = {} 63 levelStr[level] = str 64 end 65 table.insert(str, node.weight) 66 67 preOrder(level+1, node.left) 68 preOrder(level+1, node.right) 69 end 70 preOrder(1, self.root) 71 end
用20, 50, 10, 100构建的哈夫曼树:
1 function Test1() 2 local hf = HuffmanTree.new() 3 local nodeArr = { 4 HuffmanTreeNode.new(20), 5 HuffmanTreeNode.new(50), 6 HuffmanTreeNode.new(10), 7 HuffmanTreeNode.new(100), 8 } 9 hf:Build(nodeArr) 10 tostring(hf) 11 end 12 Test1()
# 编码,先生成编码查询表,一般给左子树分配0,右子树分配1,所以编码后的查询表:
20 -> 001
50 -> 01
100 -> 1
10 -> 000
# 然后编码,就直接从这种查询表直接取编码值,查不到的就是没法编码的
1 function HuffmanTree:BuildCodes() 2 self.codesQueryDict = {} 3 local stack = {} 4 5 local function preOrder(node) 6 if nil == node then return end 7 8 if node:IsLeaf() then 9 self.codesQueryDict[node.value] = table.concat(stack) 10 return 11 end 12 13 table.insert(stack, 0) 14 preOrder(node.left) 15 table.remove(stack, #stack) 16 17 table.insert(stack, 1) 18 preOrder(node.right) 19 table.remove(stack, #stack) 20 end 21 preOrder(self.root) 22 end 23 24 function HuffmanTree:Encode(arr) 25 local encodeBuilder = {} 26 for i=1,#arr do 27 local item = arr[i] 28 local codes = self.codesQueryDict[item] 29 if nil == codes then return nil end --编码失败 30 table.insert(encodeBuilder, codes) 31 end 32 local byteStr = table.concat(encodeBuilder) 33 return byteStr 34 end
# 解码,根据0为左子树,1为右子树,从根开始往下找,直到找到叶子节点;然后再从根往下找,不断重复。
1 function HuffmanTree:DecodeByString(byteStr) 2 local decodeBuilder = {} 3 local node = self.root 4 local i = 1 5 while true do 6 local nodeIsLeaf = node:IsLeaf() 7 if nodeIsLeaf then 8 table.insert(decodeBuilder, node.value) 9 node = self.root 10 end 11 12 if i > string.len(byteStr) then 13 if not nodeIsLeaf then return end --解码错误 14 break 15 end 16 17 local b = string.sub(byteStr, i, i) 18 if "0" == b then node = node.left 19 elseif "1" == b then node = node.right end 20 i = i + 1 21 end 22 23 return table.concat(decodeBuilder) 24 end
# 测试代码
1 function Test1() 2 local hf = HuffmanTree.new() 3 local nodeArr = { 4 HuffmanTreeNode.new(20), 5 HuffmanTreeNode.new(50), 6 HuffmanTreeNode.new(10), 7 HuffmanTreeNode.new(100), 8 } 9 hf:Build(nodeArr) 10 tostring(hf) 11 12 hf:BuildCodes() 13 local encodeStr = hf:Encode({10, 50}) 14 print(encodeStr) 15 print(hf:DecodeByString(encodeStr)) 16 end 17 Test1()
【其他参考】
数据结构——哈夫曼树(Huffman Tree) - 知乎 (zhihu.com)
详细图解哈夫曼Huffman编码树_无鞋童鞋的博客-CSDN博客_huffman编码树
Java 哈夫曼编码与解码_m0_38036210的博客-CSDN博客_java 哈夫曼解码
哈夫曼树以及哈夫曼编码和解码-Java实现 | 一个程序员的简单生活 (ddkiss.com)