【问题标题】:Finding the length of compressed text (Huffman coding)查找压缩文本的长度(霍夫曼编码)
【发布时间】:2018-11-01 15:12:08
【问题描述】:

给定一个包含 n 个字符的文本和一个由 Huffman 编码生成的二叉树,使得叶节点具有属性:一个字符串(字符本身)和一个整数(它在文本中的频率)。从根到任何叶子的路径代表它的代码字。

我想写一个递归函数来计算压缩文本的长度并找到它的大 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 是字符数。

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

【问题讨论】:

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


    【解决方案1】:

    要找到压缩文本的确切总长度, 我看不出有什么办法可以单独处理每个独特的角色 以及它在文本中出现的次数,总共为 O(n),其中 n 是文本中唯一字符的数量(n 也是 Huffman 树中叶节点的数量)。 有几种不同的方式来表示从霍夫曼代码到明文字母的映射。您的二叉树表示非常适合查找压缩文本的确切总长度;树中一共有 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)
    

    【讨论】:

      【解决方案2】:

      虽然找到压缩文本长度的复杂度应该是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))


      您可以通过在树中创建更简单的查找来改进二叉树:

      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'
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多