如何在此递归函数中更快地进行列表查找

时间:2016-04-06 08:35:35

标签: python

我有一个递归函数,可以创建一个json对象

def add_to_tree(name, parent, start_tree):
    for x in start_tree:
        if x["name"] == parent:
            x["children"].append({"name":name, "parent":parent, "children":[]})
        else:
            add_to_tree(name, parent, x["children"])

从另一个函数调用

def caller():
    start_tree = [{"name":"root", "parent":"null", "children":[]}] # basic structure of the json object which holds the d3.js tree data
    for x in new_list:
        name = x.split('/')[-2]
        parent = x.split('/')[-3]
        add_to_tree(name, parent, start_tree)

new_list是包含此表单链接的列表

/root/A/
/root/A/B/
/root/A/B/C/
/root/A/D/
/root/E/
/root/E/F/
/root/E/F/G/
/root/E/F/G/H/
...

除了运行时间随输入大小呈指数增长这一事实外,一切正常。 通常new_list有~500k链接,这些链接的深度可以超过10,所以add_to_tree()函数中有很多循环和外观。

关于如何加快速度的任何想法?

2 个答案:

答案 0 :(得分:2)

每次添加新条目时,您都在搜索整棵树。随着树木的增长,效率非常低;您可以轻松地以这种方式进行O(N ^ 2)搜索;对于每个新元素,再次搜索整个树。

您可以使用字典映射名称到特定树条目,以进行快速O(1)查找;这样可以避免每次都遍历树。它可以像treeindex[parent]一样简单。然而,这会占用更多内存,您可能需要处理在子项之后添加父项的情况(使用队列)。

但是,由于您的输入列表似乎已排序,您可以递归处理列表或使用堆栈并利用刚刚找到父级的事实。如果您的路径比上一个条目长,那么它将是该条目的子项。如果路径相等或更短,它将是前一个节点或该节点的父节点的兄弟条目,因此返回或弹出堆栈。

例如,对于这三个要素:

/root/A/B/
/root/A/B/C/
/root/A/D/

/root/A/B/C不必从/root/A/B的根目录中搜索树,它是先前处理的条目。这将是这个递归迭代的父调用,或者是堆栈的顶部。只需直接添加到该父级。

/root/A/D是父母的兄弟姐妹;路径短于/root/A/B/C/,因此返回或弹出堆栈的条目。长度等于/root/A/B/,所以它是一个直接的兄弟;再次返回或弹出堆栈。现在,您将处于/root/A级别,/root/A/D/是一个孩子。添加并继续您的流程。

答案 1 :(得分:2)

我还没有对此进行测试,但看起来循环在插入时没有停止,因此new_list中的每个条目都会导致遍历所有树的递归搜索。这应该加快速度:

def add_to_tree(name, parent, start_tree):
    for x in start_tree:
        if x["name"] == parent:
            x["children"].append({"name":name, "parent":parent, "children":[]})
            return True
        elif add_to_tree(name, parent, x["children"]):
            return True
    return False

一旦找到父母就会停止搜索。

那就是说,我认为这种方法存在一个漏洞。如果你有以下内容怎么办?

/root/A/B/C/
/root/D/B/E/

您的算法仅解析最后两个元素,似乎CE都将放在B下。我认为你需要考虑所有元素,然后逐个元素地沿着树元素。无论如何,这是更好的,因为你会知道在每个级别采取哪个分支,正确的版本将更快。每个插入都是O(log N)