哈夫曼编码

【理论知识可以参考】

数据结构:哈夫曼树和哈夫曼编码

 

# 哈夫曼编解码过程要依赖一棵最优二叉树,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) 

 

posted @ 2022-03-13 16:32  yanghui01  阅读(160)  评论(0编辑  收藏  举报