【问题标题】:A tree structure with dynamic depth and data in only leaves具有动态深度和仅叶子中的数据的树结构
【发布时间】:2012-07-29 01:27:25
【问题描述】:

我有以下层次结构:

- A
    - X : [1, 2, 3]
    - Y : [4, 5]
    - Z : [10, 11]
- B
    - X : [6, 7]
    - Y : [8]

我想要的是让以下查询给我以下结果:

get(A) ==> [1,2,3,4,5,10,11]
get(A,Y) ==> [4,5]
get(B) ==> [6,7,8]
get(B,X) ==> [6,7]

到目前为止,这似乎很容易。我可以通过 Dictionary> 来实现这一点,它可以是 Python 中的 defaultdict(lambda : defaultdict(list))。 但是,如果我需要使其更通用并具有另一个级别或另外 2 个级别怎么办? 类似的东西:

- A
    - X
        - i  : [1]
        - ii : [2,3]
    - Y
        - i  : [4, 5]
    - Z
        - ii : [10, 11]
- B
    - X
        - ii  : [6]
        - iii : [7]
    - Y
        - i   : [8]

在此示例中,第一个层次结构是第二个层次结构的“投影”,其中最后一个层次合并到父级中。因此,第一个层次结构的所有查询都应该给出相同的结果。

一些新级别的示例查询:

get(B, X, ii) ==> [6]
get(B,X) ==> [6,7]          (same query and result as before)

请注意,数据仅在叶节点中。因此,对于插入,必须给出整个路径:

insert(A, X, i, 20)

这也意味着,我们可以在数据结构的构造函数中给出树的深度。

编辑:我意识到我需要验证深度:

  • 插入操作:必须给出整个路径,并且 len(path) 必须等于深度
  • 获取操作:不允许使用比结构深度“更深”的路径

【问题讨论】:

  • 一般来说,遍历和操作树中元素的方式是通过递归。您可以轻松调整列表展平算法(例如 this one)来迭代节点的子节点。
  • 感谢关键字“扁平化”。用它在谷歌上搜索很容易:)

标签: python data-structures tree


【解决方案1】:
from collections import defaultdict
def tree(): return defaultdict(tree)

def get_(t):
    L = []
    if isinstance(t, list):
            L.extend(x for x in t)
    else:
        for k in t:
            L.extend(get_(t[k]))
    return sorted(L)

t = tree()
t['A']['X']['i'] = [1]
t['A']['X']['ii'] = [2,3]
t['A']['Y']['i'] = [4,5]
t['A']['Z']['ii'] = [10,11]

t['B']['X']['ii'] = [6]
t['B']['X']['iii'] = [7]
t['B']['Y']['i'] = [8]

print get_(t)
print get_(t['A'])
print get_(t['A']['X'])
print get_(t['A']['X']['i'])
print get_(t['B']['Y']['i'])

>>> 
[1, 2, 3, 4, 5, 6, 7, 8, 10, 11]
[1, 2, 3, 4, 5, 10, 11]
[1, 2, 3]
[1]
[8]
>>> 

【讨论】:

    【解决方案2】:

    看看这个:

    >>> A = Tree()
    >>> B = Tree()
    >>> A.insert_subtree("x", Leaf([1, 2, 3]))
    >>> A.insert_subtree("y", Leaf([10, 20, 30]))
    >>> B.insert_subtree("y", Leaf([100, 101, 102]))
    >>> root = Tree({'A': A, 'B': B})
    >>> root.get("A")
    [1, 2, 3, 10, 20, 30]
    >>> root.get("A", "x")
    [1, 2, 3]
    >>> root.insert("A", "x", 4)
    >>> root.get("A", "x")
    [1, 2, 3, 4]
    >>> root.get("A")
    [1, 2, 3, 4, 10, 20, 30]
    >>> root.get("B")
    [100, 101, 102]
    

    这是使它工作的代码:

    class Leaf(object):
        def __init__(self, data=None):
            self.data = data[:] if data else []
    
        def __iter__(self):
            for item in self.data:
                yield item
    
        def insert(self, value):
            self.data.append(value)
    
    
    class Tree(object):
        def __init__(self, trees=None):
            self.trees = dict(trees) if trees else {}
    
        def insert_subtree(self, name, tree):
            if name in self.trees:
                raise TreeAlreadyExists()
    
            self.trees[name] = tree
    
        def get(self, *args):
            child_name, rest = args[0], args[1:]
            child = self._get_child(child_name)
    
            if len(rest):
                return child.get(*rest)
            else:
                return [item for item in child]
    
        def _get_child(self, name):
            if name not in self.trees:
                raise KeyError("Child %s does not exist" % name)
            return self.trees[name]
    
        def insert(self, *args):
            child_name, rest = args[0], args[1:]
            child = self._get_child(child_name)
            child.insert(*rest)
    
        def __iter__(self):
            for key in sorted(self.trees.keys()):
                for item in self.trees[key]:
                    yield item
    
    class TreeAlreadyExists(Exception):
        pass
    

    【讨论】:

      【解决方案3】:

      我基于@black_dragon 的想法编写了this class,并支持深度验证。 下面是它的使用方法(复制自test case):

      def test_index_with_sample_case_for_depth_2(self):
          idx = HierarchicalIndex(2)
      
          # A
          idx.insert(1, 'A', 'X')
          idx.insert(2, 'A', 'X')
          idx.insert(3, 'A', 'X')
      
          idx.insert(4, 'A', 'Y')
          idx.insert(5, 'A', 'Y')
      
          idx.insert(10, 'A', 'Z')
          idx.insert(11, 'A', 'Z')
      
          #B
          idx.insert(6, 'B', 'X')
          idx.insert(7, 'B', 'X')
      
          idx.insert(8, 'B', 'Y')
      
          assert_that(idx.get('A'), equal_to([1, 2, 3, 4, 5, 10, 11]))
          assert_that(idx.get('A', 'Y'), equal_to([4, 5]))
          assert_that(idx.get('B'), equal_to([6, 7, 8]))
          assert_that(idx.get('B', 'X'), equal_to([6, 7]))
      
      
      def test_index_with_sample_case_for_depth_3(self):
          idx = HierarchicalIndex(3)
      
          # A
          idx.insert(1, 'A', 'X', 'i')
          idx.insert(2, 'A', 'X', 'ii')
          idx.insert(3, 'A', 'X', 'ii')
      
          idx.insert(4, 'A', 'Y', 'i')
          idx.insert(5, 'A', 'Y', 'ii')
      
          idx.insert(10, 'A', 'Z', 'ii')
          idx.insert(11, 'A', 'Z', 'iii')
      
          #B
          idx.insert(6, 'B', 'X', 'ii')
          idx.insert(7, 'B', 'X', 'iii')
      
          idx.insert(8, 'B', 'Y', 'i')
      
          #same queries with case for depth 2
          assert_that(idx.get('A'), equal_to([1, 2, 3, 4, 5, 10, 11]))
          assert_that(idx.get('A', 'Y'), equal_to([4, 5]))
          assert_that(idx.get('B'), equal_to([6, 7, 8]))
          assert_that(idx.get('B', 'X'), equal_to([6, 7]))
      
          #new queries
          assert_that(idx.get('B', 'X', 'ii'), equal_to([6]))
          assert_that(idx.get('A', 'X', 'ii'), equal_to([2, 3]))
      

      以及深度验证:

      def test_index_should_validate_depth_in_operations(self):
          # ....
          # depth=3
          idx = HierarchicalIndex(3)
      
          assert_that(idx.get('A'), has_length(0))
          assert_that(idx.get('A', 'X'), has_length(0))
          assert_that(idx.get('A', 'X', 'i'), has_length(0))
          self.assertRaises(AssertionError, lambda: idx.get('A', 'X', 'i', '1'))
      
          self.assertRaises(AssertionError, lambda: idx.insert(1))
          self.assertRaises(AssertionError, lambda: idx.insert(1, 'A'))
          self.assertRaises(AssertionError, lambda: idx.insert(1, 'A', 'X'))
          idx.insert(1, 'A', 'X', 'i')        # should not raise anything
          self.assertRaises(AssertionError, lambda: idx.insert(1, 'A', 'X', 'i', 'a'))
      
          assert_that(idx.get('A', 'X', 'i'), equal_to([1]))
      

      【讨论】:

      • 好吧,我不喜欢数据结构的名称 HierarchicalIndex。更好的名字?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-04-01
      • 1970-01-01
      • 1970-01-01
      • 2016-09-13
      • 2018-12-05
      • 1970-01-01
      相关资源
      最近更新 更多