非递归深度优先搜索算法

mou*_*sey 161 algorithm tree

我正在寻找非二叉树的非递归深度优先搜索算法.很感谢任何形式的帮助.

biz*_*lop 292

DFS:

list nodes_to_visit = {root};
while( nodes_to_visit isn't empty ) {
  currentnode = nodes_to_visit.take_first();
  nodes_to_visit.prepend( currentnode.children );
  //do something
}
Run Code Online (Sandbox Code Playgroud)

BFS:

list nodes_to_visit = {root};
while( nodes_to_visit isn't empty ) {
  currentnode = nodes_to_visit.take_first();
  nodes_to_visit.append( currentnode.children );
  //do something
}
Run Code Online (Sandbox Code Playgroud)

两者的对称非常酷.

更新:如所指出的,take_first()删除并返回列表中的第一个元素.

  • 顺便说一句,`.first()`函数也从列表中删除了元素.就像许多语言中的`shift()`一样.`pop()`也可以工作,并以从右到左的顺序而不是从左到右的顺序返回子节点. (10认同)
  • +1用于注意非递归时两者的相似程度(就好像它们在递归时它们完全不同,但仍然......) (9认同)
  • IMO,DFS算法略有不正确.想象一下3个顶点都相互连接.进展应该是:`灰色(第1) - >灰色(第2) - >灰色(第3) - >黑化(第3) - >黑化(第2) - >黑化(第1)`.但是你的代码产生:`灰色(第一) - >灰色(第二) - >灰色(第三) - >黑色(第二个) - >黑色(第三个) - >黑色(第一个). (5认同)
  • 然后为了增加对称性,如果你使用最小优先级队列作为边缘,你有一个单源最短路径查找器. (3认同)
  • @learner我可能会误解你的例子,但如果它们彼此联系在一起,那就不是一棵树了. (3认同)
  • @learner 问题是关于树的。在树中,可以保证您永远不会遇到同一个节点两次,这是该算法在处理后立即丢弃每个节点时利用的东西。如果您在非树图上进行 BFS 和 DFS,则必须以某种方式跟踪已访问过的节点,以避免两次访问同一节点。(或者永远,如果图表有循环。) (2认同)
  • @fastcodejava是的,这就是启动整个过程的原因. (2认同)

Gum*_*mbo 38

您将使用包含尚未访问的节点的堆栈:

stack.push(root)
while !stack.isEmpty() do
    node = stack.pop()
    for each node.childNodes do
        stack.push(stack)
    endfor
    // …
endwhile
Run Code Online (Sandbox Code Playgroud)

  • @Gumbo我想知道如果它是一个带有cycyles的图表.这可以吗?我想我可以避免将dulplicated节点添加到堆栈中它可以工作.我要做的是标记弹出的节点的所有邻居,并添加一个"if(节点未标记)"来判断它是否适合被推送到堆栈.那可以吗? (2认同)
  • 值得注意的是,要获得与最流行的@biziclop答案相同的代码,您需要以相反的顺序推送子注释(`for each node.childNodes.reverse()do stack.push(stack)endfor`).这也可能是你想要的.很好的解释为什么它就像在这个视频:https://www.youtube.com/watch?v = cZPXfl_tUkA endfor (2认同)

aaz*_*aaz 31

如果您有指向父节点的指针,则可以在没有额外内存的情况下执行此操作.

def dfs(root):
    node = root
    while True:
        visit(node)
        if node.first_child:
            node = node.first_child      # walk down
        else:
            while not node.next_sibling:
                if node is root:
                    return
                node = node.parent       # walk up ...
            node = node.next_sibling     # ... and right
Run Code Online (Sandbox Code Playgroud)

请注意,如果子节点存储为数组而不是通过兄弟指针存储,则可以将下一个兄弟节点存储为:

def next_sibling(node):
    try:
        i =    node.parent.child_nodes.index(node)
        return node.parent.child_nodes[i+1]
    except (IndexError, AttributeError):
        return None
Run Code Online (Sandbox Code Playgroud)

  • "如果您有指向父节点的指针,则可以在没有额外内存的情况下执行此操作":存储指向父节点的指针确实使用了一些"额外内存"... (6认同)

cor*_*iKa 5

使用堆栈跟踪节点

Stack<Node> s;

s.prepend(tree.head);

while(!s.empty) {
    Node n = s.poll_front // gets first node

    // do something with q?

    for each child of n: s.prepend(child)

}
Run Code Online (Sandbox Code Playgroud)


Max*_*ich 5

基于 biziclops 的 ES6 实现很好的答案:

root = {
  text: "root",
  children: [{
    text: "c1",
    children: [{
      text: "c11"
    }, {
      text: "c12"
    }]
  }, {
    text: "c2",
    children: [{
      text: "c21"
    }, {
      text: "c22"
    }]
  }, ]
}

console.log("DFS:")
DFS(root, node => node.children, node => console.log(node.text));

console.log("BFS:")
BFS(root, node => node.children, node => console.log(node.text));

function BFS(root, getChildren, visit) {
  let nodesToVisit = [root];
  while (nodesToVisit.length > 0) {
    const currentNode = nodesToVisit.shift();
    nodesToVisit = [
      ...nodesToVisit,
      ...(getChildren(currentNode) || []),
    ];
    visit(currentNode);
  }
}

function DFS(root, getChildren, visit) {
  let nodesToVisit = [root];
  while (nodesToVisit.length > 0) {
    const currentNode = nodesToVisit.shift();
    nodesToVisit = [
      ...(getChildren(currentNode) || []),
      ...nodesToVisit,
    ];
    visit(currentNode);
  }
}
Run Code Online (Sandbox Code Playgroud)