给出由霍夫曼编码生成的n个字符和二叉树的文本,使得叶节点具有属性:字符串(字符本身)和整数(其在文本中的频率)。从根到任何叶子的路径代表其代码字。
我想编写一个重复函数来计算压缩文本的长度并找到它的大O-复杂度。
例如,如果我有文字
abaccab
并且每个角色在霍夫曼树中都有相关的频率和深度:
4
/ \
a:3 5
/ \
b:2 c:2
然后压缩文本的总长度是11
我想出了这个,但看起来很粗糙:
def get_length(node, depth):
#Leaf node
if node.left_child is None and node.right_child is None:
return node.freq*depth
#Node with only one child
elif node.left_child is None and node.right_child is not None:
return get_length(node.right_child, depth+1)
elif node.right_child is None and node.left_child is not None:
return get_length(node.left_child, depth+1)
#Node with two children
else:
return get_length(node.left_child, depth+1) + get_length(node.right_child, depth+1)
get_length(root,0)
复杂性:O(log 2n)其中n是字符数。
我该如何改进?这种情况的复杂性是什么?
答案 0 :(得分:0)
虽然找到压缩文本长度的复杂性应O(n)
(使用简单len
),但完成编码的时间复杂度应为O(nlog(n))
。算法如下:
t1 = FullTree
for each character in uncompressed input do: #O(n)
tree_lookup(t1, character) #O(log(n))
循环未压缩的输入为O(n)
,而在平衡二叉树中查找节点为O(log(n))
(O(n)
最坏情况或其他情况)。因此,结果是n*O(log(n)) => O(nlog(n))
。另请注意,O(log 2n)
对于查找的复杂性是准确的,因为对数规则可以简化为O(log(2)+log(n)) => O(k + log(n)), for some constant k.
但是,因为Big-O仅检查最差情况近似值O(k+log(n)) => O(log(n))
。< / p>
您可以通过在树中创建更简单的查找来改进二叉树:
from collections import Counter
class Tree:
def __init__(self, node1, node2):
self.right = node1
self.left = node2
self.value = sum(getattr(i, 'value', i[-1]) for i in [node1, node2])
def __contains__(self, _node):
if self.value == _node:
return True
return _node in self.left or _node in self.right
def __lt__(self, _node): #needed to apply sorted function
return self.value < getattr(_node, 'value', _node[-1])
def lookup(self, _t, path = []):
if self.value == _t:
return ''.join(map(str, path))
if self.left and _t in self.left:
return ''.join(map(str, path+[0])) if isinstance(self.left, tuple) else self.left.lookup(_t, path+[0])
if self.right and _t in self.right:
return ''.join(map(str, path+[1])) if isinstance(self.right, tuple) else self.right.lookup(_t, path+[1])
def __getitem__(self, _node):
return self.lookup(_node)
s = list('abaccab')
r = sorted(Counter(s).items(), key=lambda x:x[-1])
while len(r) > 1:
a, b, *_r = r
r = sorted(_r+[Tree(a, b)])
compressed_text = ''.join(r[0][i] for i in s)
输出:
'10110000101'
答案 1 :(得分:0)
要找到压缩文本的确切总长度, 我看不到必须单独处理每个唯一字符的方法 以及它在文本中出现的次数,这是总数O(n),其中n是文本中唯一字符的数量(也是n是霍夫曼树中叶节点的数量)。 从霍夫曼码到明文字母的映射有几种不同的表示方法。您的二叉树表示形式非常适合查找压缩文本的确切总长度;树中总共有2 * n-1个节点,其中n是文本中唯一字符的数量,并且对每个节点的递归扫描需要2 * n-1次,这也等于总计O(n)。
def get_length(node, depth):
#Leaf node
if node.left_child is None and node.right_child is None:
return node.freq*depth
#null link from node with only one child, either left or right:
elif node is None:
print("not a properly constructed Huffman tree")
return 0
#Node with two children
else:
return get_length(node.left_child, depth+1) + get_length(node.right_child, depth+1)
get_length(root,0)