根据树的关系创建嵌套列表

时间:2019-01-31 20:03:03

标签: python algorithm

我有一个数据类型,该数据类型由一个列表中的多个元组组成。它代表了亲子关系。

例如[('A', 1), ('A', 2, 1), ('A', 2, 2) ('A', 3), ('B', 1), ('B', 1, 1), ('B', 1, 2), ('C',)],其中元组可以具有1、2或3个项目,其格式为(字母,数字,数字)。在上面的示例中,('B', 1)('B', 1, 1)('B', 1, 2)的父级,依此类推,直到我们得到一个字母为止。

我的问题是,我该如何创建一个函数来接收类似上面示例的内容,并创建一个嵌套列表,其中将相似的顺序和字母/数字组合在一起。

例如,如何创建类似以下内容的函数:

[('A', 1), ('A', 2, 1), ('A', 2, 2), ('A', 3), ('B', 1), ('B', 1, 1), ('B', 1, 2), ('B', 2), ('B', 3), ('C',)]

并将其转换为:

[[('A', 1), [('A', 2, 1), ('A', 2, 2)] ('A', 3)], [[('B', 1, 1), ('B', 1, 2)], ('B', 2), ('B', 3)], ('C',)]

还请注意,该列表将按照字母和数字顺序进行预排序。输入列表中也只有最低阶元组。 (如果存在父级元组,则它们不会出现在输入列表中)

谢谢!

2 个答案:

答案 0 :(得分:0)

我们基本上可以遍历元组,并为每个元组递归地“潜入”数据结构,并添加该元素。但是我认为,至少对于中间结构,列表是不合适的。字典允许快速检索,因此将促进更新。

def to_nested_list(tuples):
    data = {}
    for tup in tuples:
        elem = data
        for ti in tup:
            elem = elem.setdefault(ti, {})
    stck = []
    def to_list(source, dest):
        for k, v in source.items():
            stck.append(k)
            if v:
                dest.append(to_list(v, []))
            else:
                dest.append(tuple(stck))
            stck.pop()
        return dest
    return to_list(data, [])

因此,对于给定的样本数据,我们首先构造一个字典,该字典在stck = []行之前看起来像:

{'A': {1: {}, 2: {1: {}, 2: {}}, 3: {}}, 'B': {1: {1: {}, 2: {}}}, 'C': {}}

接下来,我们通过递归遍历字典来“收获”该结构的元组,并且每次相应的值 not 为空时,添加一个基于“调用路径”构造的元组”添加到相应的子列表。

例如:

>>> to_nested_list([('A', 1), ('A', 2, 1), ('A', 2, 2), ('A', 3), ('B', 1), ('B', 1, 1), ('B', 1, 2), ('C',)]) 
[[('A', 1), [('A', 2, 1), ('A', 2, 2)], ('A', 3)], [[('B', 1, 1), ('B', 1, 2)]], ('C',)]

这适用于任意长度的元组,只要这些元组的元素都是 hashable (字符串和整数是可哈希的,因此如果元组仅包含字母,则在这里是安全的 s和 number s)。

话虽如此,但我不确定使用嵌套列表是否是个好主意。这样的列表将导致以下事实:验证列表包含某个元组可能要花费大量时间,因为列表的元素不会“提示”该元组的前缀。我认为data字典可能是更好的表示形式。

答案 1 :(得分:0)

设置

a = [('A', 1), ('A', 2, 1), ('A', 2, 2), ('A', 3), ('B', 1), ('B', 1, 1), ('B', 1, 2), ('B', 2), ('B', 3), ('C',)]

以下解决方案适用于任何深度的树木:

首先,一个辅助函数,如果需要,可以用多余的括号将每个节点包裹起来

def self_wrap(x, n):
    output = x
    for _ in range(n):
        output = [output]
    return output

现在,主循环:

out_list = []
for i in range(len(a)):
    # add 0th element to out_list
    if i == 0:
        out_list.append(self_wrap(a[i], len(a[i])-1))
        continue

    # determine the appropriate bracket level to add a[i]
    prev_list = curr_list = out_list
    j = 0
    while min(len(a[i-1]), len(a[i])) > j and a[i-1][j] == a[i][j]:
        prev_list, curr_list = curr_list, curr_list[-1]
        print(curr_list, i, j)
        j += 1
    left_over_len = len(a[i]) - j - 1

    # override if last item was parent
    if j == len(a[i-1]):
        prev_list[-1] = self_wrap(a[i], left_over_len + 1)
        continue

    # append a[i] to appropriate level and wrap with additional brackets if needed
    curr_list.append(self_wrap(a[i], left_over_len) if left_over_len > 0 else a[i])

print(out_list)

此打印

[[('A', 1), [('A', 2, 1), ('A', 2, 2)], ('A', 3)], [[('B', 1, 1), ('B', 1, 2)], ('B', 2), ('B', 3)], ('C',)]

符合预期。


正如人们指出的那样,这种结构不是很有效。有两个原因:

  1. 冗余信息
  2. 列表很难操作/查找

话虽如此,可能是表示路径的唯一方法。