如何将成对依赖转换为多路树

时间:2018-03-28 12:37:06

标签: algorithm tree

例如,有以下依赖: A< -B,C< -H,B< -F,B< -G,A< -C&C,A< -D,A< -E 我想将这些依赖转换为这样的多路树:

      A
   / /  \ \
  B  C   D E
 /\ /
F G H

有人会有一个好的方法吗?

1 个答案:

答案 0 :(得分:0)

python中的解决方案。

每个依赖都是从孩子到他父母的边缘,例如A <- B表示节点B是节点A的子节点,并且您有BA的边缘。 话虽这么说,您需要一个数据结构来存储这些节点和边。

普通老字典

第一个解决方案是一个简单的词典。我们走吧。

首先,您必须以可用的格式解析依赖项。我提出了一种格式(父,子)和一个列表解析来解析字符串(这很容易翻译成任何高级语言):

def get_deps(dep_strings):
    """Return  list of tuples (parent, child). Very rudimentary."""
    return [tuple(dep_list) for dep_string in dep_strings.split(",") for dep_list in [dep_string.strip().split("<--")]]

其次,你必须遍历依赖项来构建树(这是算法的核心,并且很容易在任何高级语言中翻译):

def parse_to_dict(dep_strings):
    deps = get_deps(dep_strings)
    # the dict will take associate to every parent its "children dictionaries"
    d = {}
    for (p, c) in deps:
        if p not in d: # parent has not yet been seen
            d[p] = {}
        if c not in d: # child has not yet been seen
            d[c] = {} # child is a leaf, until it becomes possibly a parent
        d[p][c] = d[c] # hang the child to its parent.

    # the dict has a key for every node, and the value is its "children dictonaries".
    # usually, we don't need this direct access, thus we remove all non root elements
    for _, c  in deps:
        del d[c]

    return d

Python特定

由于为每个未知节点创建一个空的dict很麻烦,您可以使用defaultdictdict.setdefault使其更具可读性。

...
d = defaultdict(dict) # will create an empty dict for every missing key
for (p, c) in deps:
    d[p][c] = d[c] # hang the child to its parent, create dicts on demand.

或者

...
d = {}
for (p, c) in deps:
    d.setdefault(p, {}) # will create an empty dict if this key is missiing
    d.setdefault(c, {})
    d[p][c] = d[c] # hang the child to its parent, create dicts on demand.

请注意dict.setdefault更灵活,因为它可以接受参数,稍后您将看到。

自定义树

如果要构建自定义树,则必须创建Node类,例如:

class Node():
    """A very simple node class"""
    def __init__(self, id):
        self.__id = id
        self.__children = []

    def add(self, n):
        self.__children.append(n)

    def __str__(self):
        if self.__children:
            return "Node {} [{}]".format(self.__id, ", ".join(str(c) for c in self.__children))
        else:
            return "Leaf {}".format(self.__id)

创建树将与字典一样简单:

...
# we still need a dict
d = {}
for (p, c) in deps:
    d.setdefault(p, Node(p)) # we need to build a node with an id,
    d.setdefault(c, Node(c)) #  hence defaultdict is not the right tool
    d[p].add(d[c])

不同方法的输出

# dict
{'A': {'B': {'F': {}, 'G': {}}, 'C': {'H': {}}, 'D': {}, 'E': {}}}
# defaultdict
defaultdict(<class 'dict'>,
            {'A': {'B': {'F': {}, 'G': {}}, 'C': {'H': {}}, 'D': {}, 'E': {}}})
# dict.setdefault
{'A': {'B': {'F': {}, 'G': {}}, 'C': {'H': {}}, 'D': {}, 'E': {}}}
# Node
Node A [Node B [Leaf F, Leaf G], Node C [Leaf H], Leaf D, Leaf E]