访问链接树的所有节点的最佳方法是什么(所有节点都引用了父节点和所有子节点,根节点的空节点为null),以便在任何节点之前没有访问任何节点?布朗尼指出非递归。
答案 0 :(得分:6)
伪代码:
NodesToVisit = some stack or some list
NodesToVisit.Push(RootNode)
While NodesToVisit.Length > 0
{
CurNode = NodesToVisit.Pop()
For each Child C in CurNode
NodesToVisit.Push(C)
Visit(CurNode) (i.e. do whatever needs to be done)
}
修改:递归与否?
为了在技术上正确,并且正如AndreyT和其他人在本文中指出的那样,这种方法 一种 递归算法的形式,其中使用明确管理的堆栈代替CPU堆栈和递归发生在While循环的级别。这就是说,它与递归实现本身的区别在于几种微妙而重要的方式:
答案 1 :(得分:5)
答案 2 :(得分:3)
您正在寻找前序遍历。我认为你可以非递归地使用它 一个队列:在伪代码中:
创建一个空队列,然后按下根节点。
while nonempty(q)
node = pop(q)
visit(node)
foreach child(node)
push(q,child)
答案 3 :(得分:3)
如果你有所有孩子和父母的链接,那么非递归算法相当简单。忘记你有一棵树。可以想象它是一个实验室,每个亲子链接都是从一个交汇点到另一个交汇点的普通双向走廊。走完整个迷宫所需要做的就是转入每个交叉路口左侧的下一个走廊。 (或者,将它想象为走在迷宫中,左手始终触及左侧的墙壁)。如果你从根交叉点开始(并向任何方向移动),你将走遍整个树,总是在孩子面前访问父母。在这种情况下,每个“走廊”将被移动两次(在一个方向和另一个方向),并且每个“交叉点”(节点)将被访问多次“走廊”加入它。
答案 4 :(得分:1)
使用一组节点。将根放在集合中以开始。然后在循环中,将一个节点拉出集合,访问它,然后将其子节点放入集合中。当集合为空时,您就完成了。
答案 5 :(得分:1)
在伪代码中:
currentList = list( root )
nextList = list()
while currentList.count > 0:
foreach node in currentList:
nextList.add(node.children)
currentList = nextList
答案 6 :(得分:1)
如果从根节点开始,只访问已访问过的节点的父节点/子节点,则无法遍历树,以便在访问其祖先之前访问节点
任何类型的遍历,深度优先(基于递归/堆栈),广度优先(基于队列),深度限制,或者只是将它们从无序集中拉出来都可以。
“最佳”方法取决于树。宽度优先适用于树枝很少的高大树。对于有许多树枝的树木,深度首先适用。
由于节点实际上有指向父节点的指针,因此还有一个常量内存算法,但速度要慢得多。
答案 7 :(得分:1)
我不同意广度优先搜索,因为空间复杂性通常是特定搜索算法的祸根。可能使用迭代加深算法是这种类型的使用的更好的替代方案,并且它涵盖与广度优先搜索相同类型的遍历。在处理广度优先搜索的边缘方面存在细微的差别,但是(伪)编码不应该太难。
答案 8 :(得分:1)
这是真正的非递归方法:没有堆栈,不变空间。这个Python代码假设每个节点都包含一个子节点列表,并且节点对象没有定义相等性,因此'index'函数正在比较标识:
def walkTree(root, visit_func):
cur = root
nextChildIndex = 0
while True:
visit_func(cur)
while nextChildIndex >= len(cur.children) and cur is not root:
nextChildIndex = cur.parent.children.index(cur) + 1
cur = cur.parent
if nextChildIndex >= len(cur.children):
break
cur = cur.children[nextChildIndex]
nextChildIndex = 0
我确信它可以被打磨一下,更简洁,更容易阅读,但这就是要点。
答案 9 :(得分:0)
在根(级别0)构建节点列表,依次遍历每个节点并查找直接子节点(其父节点是我们当前正在查看的节点)(级别1),当完成级别时0继续迭代1级,依此类推,直到你没有剩余的未访问节点。