我有一个递归函数,可以创建一个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()
函数中有很多循环和外观。
关于如何加快速度的任何想法?
答案 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/
您的算法仅解析最后两个元素,似乎C
和E
都将放在B
下。我认为你需要考虑所有元素,然后逐个元素地沿着树元素。无论如何,这是更好的,因为你会知道在每个级别采取哪个分支,正确的版本将更快。每个插入都是O(log N)
。