我正在学习编码访谈和使用大量不同的数据结构。
我对树问题比较陌生,每天都在做练习。
让公式致力于记忆是一回事,而另一个是真正理解它们。当我理解某些东西时,很容易将这种理解应用于更困难的问题。
递归解决方案对我来说有点难以进行心理可视化,而直觉上它们是有意义的,我试图深入了解堆栈上发生的事情。
我有一棵树,想按顺序进行遍历。没问题。
data = []
def checkBST(root):
if root:
checkBST(root.left)
data.append(root.data)
checkBST(root.right)
print(data)
我创建了数据变量来打印出通过该方法每次传递时存储的内容。
打印
[]
[1]
[1]
[1, 2]
[1, 2, 3]
[1, 2, 3]
[1, 2, 3]
[1, 2, 3, 4]
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5, 6]
[1, 2, 3, 4, 5, 6, 7]
[1, 2, 3, 4, 5, 6, 7]
[1, 2, 3, 4, 5, 6, 7]
[1, 2, 3, 4, 5, 6, 7]
我正在尝试从逻辑上看看发生了什么,并想知道我的逻辑是否正确。
共有15个打印结果和7个节点。但是我们得到15,因为有8个位置检查Node是None
的节点。这发生在节点1,3,5,7上。
我们正在检查右边的树的左半部分。
[]
#nothing stored because we move onto Node 2 as we don't hit the base case.
[1]
#1 stored because Node 1 doesn't have a left value. So we move onto the append call.
[1]
#1 returned because Node 1 doesn't have a right value.
[1, 2]
#2 stored because because we finished checking the left side and moved onto append data.
[1, 2, 3]
#3 is stored because we are calling the in order traversal on the right side of two now.
[1, 2, 3]
#3 is returned again because it doesn't have a root.left
[1, 2, 3]
#3 is returned again because it doesn't have a root.right
[1, 2, 3, 4]
# we hit the append method for 4, now we move onto the in order traversal on the right
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5, 6]
[1, 2, 3, 4, 5, 6, 7]
[1, 2, 3, 4, 5, 6, 7]
[1, 2, 3, 4, 5, 6, 7]
[1, 2, 3, 4, 5, 6, 7]
右侧将像左侧一样被检查,所以我没有写出我的逻辑,因为它是多余的。
如果我以正确的格式查看此问题,我只想澄清一下。
感谢您对此有任何帮助!
答案 0 :(得分:3)
输出中的注释并不总是正确的。
当函数调用到达结束时,会发生第一个输出([]
)。发生这种情况的第一个调用是root
是节点1并且从那里进行第一次递归调用。该调用将以None
作为参数,因此这是第一次调用到达print
语句。
所以我们有这些正在进行的电话:
checkBST(4)
checkBST(2) # left child of 4
checkBST(1) # left child of 2
checkBST(None) # left child of 1
print # --> []
当最深的调用结束时,具有节点1的函数将1附加到数据列表,然后对正确的子项进行递归调用,同时打印None
和[1]
。
以下是该过程的可视化。列表示递归的深度,行表示随时间推移(向下)的事件序列。最后一列保留用于显示data
的当前值。当它具有黄色背景时,表示它已被打印。
淡蓝色表示代码在该递归深度处执行。深蓝色表示相应的函数调用挂起(在堆栈上),等待嵌套的递归调用返回。
从这张图片中你还可以看到为什么有时会重复相同的输出:当算法回溯时,它会以不同的递归级别打印。