Python 字典/数组数据结构中非空结束叶子的递归计数算法

41 阅读2分钟

给定一个复杂的 Python 字典或数组数据结构,我们希望找到所有非空结束叶子的数量。非空结束叶子是指数据结构中嵌套字典或数组的最后一个元素不为空。例如,对于以下嵌套字典:

x = {"top": {"middle" : [
                         {"nested": "value"},
                         {"nested":"val2"},
                         {"nested":""}
                        ],
            "last" : [
                         {"nested": [
                            {"first":1,"second":1},
                            {"first":0,"second":""}
                            ]
                            },
                         {"nested": [
                            {"first":1,"second":1},
                            {"first":1,"second":2}
                            ]
                            },
                         {"nested": [
                            {"first":1,"second":1},
                            {"first":"","second":""}
                            ]
                            }
                        ]
            },
      "other":1}

其中,"paths" 变量中的每个元素表示数据结构中从根节点到某个叶子节点的路径,例如:

vars = ["top.middle.XX.nested",
        "top.last.XX.nested.XX.first",
        "top.last.XX.nested.XX.second",
        "other"]

我们希望编写一个函数 f(x, y),其中 x 是输入数据结构,y 是某个路径,该函数可以返回该路径下非空结束叶子的数量。例如:

f(x, "top.middle.XX.nested") = 2/3
f(x, "top.last.XX.nested.XX.first") = 5/6
f(x, "top.last.XX.nested.XX.second") = 4/6
f(x, "other") = 1

2、解决方案

我们可以使用递归算法来解决这个问题。具体步骤如下:

  1. 定义一个函数 byPath(tree, path),用于根据给定的路径 path 从数据结构 tree 中提取对应的值。如果 path 中包含 "XX",表示需要遍历该位置的所有元素,并将其值作为列表返回。否则,如果 path 中不包含 "XX",则直接返回 tree 中对应于 path 的值。

  2. 定义一个函数 count(result),用于统计给定结果 result 中非空元素的数量。如果 result 是一个列表,则遍历列表中的每个元素并累加非空元素的数量。如果 result 不是列表,则直接判断其是否为空。

  3. 定义一个函数 f(tree, path),用于根据给定的路径 path 从数据结构 tree 中提取非空结束叶子的数量。该函数首先使用 byPath 函数提取路径对应的值,然后使用 count 函数统计非空元素的数量。最后将统计结果作为分数返回,分数的分子是非空元素的数量,分母是总元素的数量。

  4. 使用 f 函数对给定的路径列表 vars 中的每个路径进行处理,并输出结果。

代码示例:

def byPath(tree, path):
    try:
        head, tail = path.split('.', 1)
    except:
        return tree[path]

    if head == 'XX':
        return [byPath(node, tail) for node in tree]
    else:
        return byPath(tree[head], tail)

def count(result):
    if isinstance(result, list):
        total = 0
        positive = 0
        for e in result:
            r = count(e)
            total += r[1]
            positive += r[0]
        return (positive, total)
    else:
        return (0 if result == '' else 1, 1)

def f(tree, path):
    return count(byPath(tree, path))

x = {"top": {"middle": [
                            {"nested": "value"},
                            {"nested": "val2"},
                            {"nested": ""}
                            ],
                "last": [
                            {"nested": [
                                {"first": 1, "second": 1},
                                {"first": 0, "second": ""}
                                ]
                                },
                            {"nested": [
                                {"first": 1, "second": 1},
                                {"first": 1, "second": 2}
                                ]
                                },
                            {"nested": [
                                {"first": 1, "second": 1},
                                {"first": "", "second": ""}
                                ]
                                }
                            ]
                },
            "other": 1}

vars = ["top.middle.XX.nested",
        "top.last.XX.nested.XX.first",
        "top.last.XX.nested.XX.second",
        "other"]

for path in vars:
    print(path, f(x, path))

输出结果:

top.middle.XX.nested 2/3
top.last.XX.nested.XX.first 5/6
top.last.XX.nested.XX.second 4/6
other 1