查找压缩文本的长度(霍夫曼编码)

时间:2018-05-22 18:53:42

标签: python time-complexity binary-tree huffman-code

给出由霍夫曼编码生成的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是字符数。

我该如何改进?这种情况的复杂性是什么?

2 个答案:

答案 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)