Hướng dẫn python build tree from parent-child list - python xây dựng cây từ danh sách cha-con

Giả sử nó được cư xử tốt (không có chu kỳ, không có tên trùng lặp hoặc nhiều cha mẹ cho một đứa trẻ), bạn có thể chỉ cần sử dụng "biểu đồ có hướng" và đi qua nó. Để tìm (các) gốc, tôi cũng đã sử dụng một từ điển có chứa boolean cho biết nếu có bất kỳ cha mẹ nào cho tên:

lst = [('john','marry'), ('mike','john'), ('mike','hellen'), ('john','elisa')]

# Build a directed graph and a list of all names that have no parent
graph = {name: set() for tup in lst for name in tup}
has_parent = {name: False for tup in lst for name in tup}
for parent, child in lst:
    graph[parent].add(child)
    has_parent[child] = True

# All names that have absolutely no parent:
roots = [name for name, parents in has_parent.items() if not parents]

# traversal of the graph (doesn't care about duplicates and cycles)
def traverse(hierarchy, graph, names):
    for name in names:
        hierarchy[name] = traverse({}, graph, graph[name])
    return hierarchy

traverse({}, graph, roots)
# {'mike': {'hellen': {}, 'john': {'elisa': {}, 'marry': {}}}}

FromCollectionSimportDefaultDicTroot = -1DefCreate_Tree (cặp): "" "Cho một số nhận dạng (cha, con) Đối với các nút lá). Nhiều nút gốc được hỗ trợ; tất cả các gốc được đặt dưới nút gốc tổng hợp. Childinpairs: Bảng [cha mẹ] [1] .Append (con) # cha mẹ -> con } Parent.Append (con) IfCintable: Theo dõi (trống, Bảng [c] [1]) # điền vào đệ quy vào treetree = {root: []} roots = [kfork, Vintable.items () ifv [0] == Root] theo dõi (cây [root], roots) returnTree ## kiểm tra: #def_build_test_tree (nút): def_build (nút, cây): iflen (nút) == 0: return [] other: return [{nút [0]: _Build (nút [1:], cây)}] return_bu ILD (các nút, {}) [0] defassertequals (thực tế, dự kiến): assertActual == mong đợi, "%s! =%s"%(thực tế, dự kiến) . 2,3]) thực tế = created_tree ([(1,2), (2,3)]) = created_tree ([(1,2), (3,4)]) dự kiến ​​= {root: [{1: [hai]}, {3: [bốn]}]} assertequals (thực tế, dự kiến) mong đợi = _Build_test_tree ([root, 1,2,3]) thực tế = created_tree ([(2,3), (1,2)]) , 2,3,4]) thực tế = created_tree ([(3,4), (2,3), (1,2)]) Điều này ổn nhưng khó kiểm tra. ## TODO khôi phục bài kiểm tra này sau khi thay đổi thành {id : 1, tên: 'foo', trẻ em: []} Định dạng ... ## DEF test_order_indepent (): ## Từ nhập ngẫu nhiên shuffle ## cặp = [(1,2), (2,3), (2 , 4), (1,5), (6,7), (6,8), (8,9)] ## Kết quả = [] ## cho i trong phạm vi (10): ## shuffle (cặp) ## results.append (created_tree (cặp)) ## cho i trong phạm vi (9): ## kết quả khẳng định [i] == kết quả [i+1], "thứ tự đầu vào khác nhau cho kết quả khác nhau: %s! = % s " % (kết quả [i], kết quả [i+1]) if__name __ ==" __ main__ ": tests = [(k, v) nĩa . collections import defaultdict ROOT = -1 def create_tree(pairs): """ Given an iterable of (parent, child) identifiers, build a tree where each node is a dict whose key is its identifier, and whose value is a list of children (which may be empty for leaf nodes). Multiple root nodes are supported; all roots are placed under a synthetic ROOT node. """ def node(): return [ROOT,[]] table = defaultdict(node) # Build 2-way mapping between nodes for parent, child in pairs: table[parent][1].append(child) # parent - > children table[child][0] = parent # child -> parent def follow(parent, childids): for c in childids: empty = [] child = {c: empty} parent.append(child) if c in table: follow(empty, table[c][1]) # Recursively fill in the tree tree = {ROOT:[]} roots = [k for k,v in table.items() if v[0] == ROOT] follow(tree[ROOT], roots) return tree # # TESTS: # def _build_test_tree(nodes): def _build(nodes, tree): if len(nodes) == 0: return [] else: return [{nodes[0]: _build(nodes[1:], tree)}] return _build(nodes, {})[0] def assertEquals(actual, expected): assert actual == expected, "%s != %s" % (actual, expected) def test_empty(): assert create_tree([]) == {ROOT:[]} def test_one(): expected = _build_test_tree([ROOT,1,2]) actual = create_tree([(1,2)]) assertEquals(actual, expected) def test_chain(): expected = _build_test_tree([ROOT,1,2,3]) actual = create_tree([(1,2),(2,3)]) assertEquals(actual, expected) def test_forked_root(): two = {2: []} four = {4: []} actual = create_tree([(1,2),(3,4)]) expected = {ROOT: [{1: [two]},{3: [four]}]} assertEquals(actual, expected) def test_replace_root(): expected = _build_test_tree([ROOT,1,2,3]) actual = create_tree([(2,3),(1,2)]) assertEquals(actual, expected) def test_splice(): expected = _build_test_tree([ROOT,1,2,3,4]) actual = create_tree([(3,4),(2,3),(1,2)]) assertEquals(actual, expected) ## Fails because the order of the lists may vary, which is OK but hard to test. ## TODO restore this test after changing to {id: 1, name: 'foo', children: []} format... ##def test_order_independent(): ## from random import shuffle ## pairs = [(1,2),(2,3),(2,4),(1,5),(6,7),(6,8),(8,9)] ## results = [] ## for i in range(10): ## shuffle(pairs) ## results.append(create_tree(pairs)) ## for i in range(9): ## assert results[i] == results[i+1], "Different input order gives different results: %s != %s" % (results[i], results[i+1]) if __name__ == "__main__": tests = [(k, v) for k, v in locals().items() if k.startswith("test")] print("%s TESTS FOUND" % len(tests)) for k, v in tests: print(k) v() print("TESTS COMPLETE")