【问题标题】:Depth of a tree using DFS使用 DFS 的树的深度
【发布时间】:2012-05-20 02:33:34
【问题描述】:

我正在尝试使用 DFS 而不是 BFS 在 Python 中编写代码,以返回每个节点有任意数量的子节点的树中最深叶的深度。看来我已经接近了,但是下面的代码仍然有一些我无法弄清楚的错误(即返回的深度不正确)。有什么帮助吗?

测试树将是:[[1,2,3],[4,5],[6],[7],[8],[],[],[],[]]

def max_depth_dfs(tree): # DOESN'T WORK

    max_depth, curr_depth, Q = 0,0, [0]
    visited = set()

    while Q != []:
        n = Q[0]
        more = [v for v in tree[n] if v not in visited]
        if not more:
            visited.add(n)
            curr_depth -= 1
            Q = Q[1:]
        else:
            curr_depth += 1

        max_depth = max(max_depth, curr_depth)
        Q = more + Q

    return max_depth

【问题讨论】:

  • 只是为了确保问题出在代码和对需求的解释中:您希望 [[1,2,3],[4, 5],[6],[7],[8],[],[],[],[]]?返回的代码是什么?
  • 是的,好点。我期望节点 0 的深度为 0,该特定树的深度为 3。 [[1],[]] 的深度应该是 1,[[1],[2] 的深度,[]] 应该是 2 等等。
  • 对于那个输入,这确实返回 3 Repl.it proof
  • 你是对的,但这是一个幸运的案例。 [[1,2,3],[4,5],[6],[7],[],[],[8],[],[]] 给出 2,应该是 3。我是在第一个示例中跟踪“curr_depth”,虽然它给出了 3,但 curr_depth 不正常。
  • 我对树形结构有点困惑。整数是带数据值的叶节点,数组是分支节点吗?您的示例都没有显示进一步的嵌套,例如 [[1] [2 [3, 4]]]。允许吗?

标签: python tree


【解决方案1】:

我发现了错误!

if not more:
    visited.add(n)
    curr_depth -= 1
    Q = Q[1:]

当您访问节点 4 时,curr_depth 等于 2。节点 4 没有子节点,因此您减小 curr_depth 并且现在 curr_depth 等于 1。但是,您将访问的下一个节点是节点 5,并且节点 5 的深度是 2 而不是 1。因此,curr_depth 不会记录该节点在树中的正确深度。

以下解决方案可能会有所帮助。

def max_depth_dfs(tree):

    max_depth, curr_depth, Q = 0, 0, [0]
    visited = set()

    while Q != []:
        n = Q[0]

        max_depth = max(max_depth, curr_depth)

        if n in visited:
            curr_depth -= 1
            Q = Q[1:]
            continue

        #print n, curr_depth     #show the node and its depth in the tree

        visited.add(n)
        more = [v for v in tree[n]]
        if not more:
            Q = Q[1:]
        else:
            curr_depth += 1
            Q = more + Q

    return max_depth

【讨论】:

  • 我意识到保持“已访问”数据结构意味着我们正在将树“复制”到该数据结构。也就是在执行结束的时候,所有的节点在“tree”中存储一次,在“visited”中存储一次……有没有可能做得更好?
  • 您可以只记录被访问节点的索引,而不是将节点复制到“访问”数据结构中。
  • 是的,不复制整个节点本身。我更多地考虑根本没有“访问过”的数据结构,既不是整个节点,也不是对节点的引用。
  • 递归版本可以做到这一点,但我认为这不是一个好主意。
  • 我想知道我们是否不能利用 DFS 在树中前进的方式来直接“知道”哪些节点已被访问。另外,当你在树的“右边”的某个地方时,你不需要记住你在“左边”访问了哪些节点(因为 DFS 永远不会回到那里)。实际上,例如,每次返回根目录时,都可以清除“已访问”(并减少内存使用)。这是真的吗?
【解决方案2】:

我使用try .. catch 来区分树枝和树叶。 更新没有更多例外:)

from collections import Iterable
tree = [[1,2,3],[4,5, [1, 6]],[6],[7],[8],[],[],[],[]]

def max_depth(tree, level=0):
  if isinstance(tree, Iterable):
    return max([ max_depth(item, level+1) for item in tree])
  else: # leaf
    return level

print max_depth(tree)

【讨论】:

  • 由于堆栈限制,我特别不想要这个递归版本。我想使用我自己的堆栈(Q)。我的递归版本为: def max_depth(tree): def _depth(node): return 0 if tree[node] == [] else 1 + max(_depth(v) for v in tree[node]) return _depth( 0)
  • 我还建议不要使用 except 子句,不设置任何异常来捕获,因为这几乎不可能以任何方式中断函数的执行
【解决方案3】:

这是非递归版本:

from collections import Iterable

def max_depth_no_recur(tree):
  max_depth, node =  0, iter(tree)
  stack = [node]
  while stack:
    try:
      n = node.next()
    except StopIteration:
      if len(stack) > max_depth:
        max_depth = len(stack)
      node = stack.pop()
      continue

    if isinstance(n, Iterable):
        stack.append(node)
        node = iter(n)
  return max_depth

【讨论】:

  • 这个很好,但是机器很多。我希望有更简单的东西:-)(即没有Iterable,也没有isinstance?)
  • 以前的版本不行。这个可以。 Iterable只是判断节点是否可以挖dipper。
  • 是的,这就是为什么我将节点 id 留在堆栈上,直到它的所有子节点都被探索完:只有当该节点上没有任何东西可以探索时,我才会执行 Q = Q[1:]。
  • 这个版本非常节省内存。它仅在本地堆栈上保存惰性迭代器,并进行深度优先遍历。
  • 是的,迭代器非常高效。
【解决方案4】:

在考虑了我从 Alex 和 Adonis 获得的所有良好反馈并改进代码后,我目前拥有当前版本:

def max_depth_dfs(tree): # correct

max_depth, curr_depth, Q = 0, 0, [0]
visited = set()

while Q != []:

    n = Q[0]

    if n in visited:
        Q = Q[1:]
        curr_depth -= 1
        visited.remove(n) # won't go back, save memory 
        print 'backtrack from', n        
        continue

    # proper place to print depth in sync with node id
    print 'visiting', n, 'children=', tree[n], 'curr_depth=', curr_depth, 'Q=', Q,
    print visited # only current path, instead of visited part of tree

    if tree[n]:
        visited.add(n) # if leaf, won't ever try to revisit
        Q = tree[n] + Q
        curr_depth += 1
        max_depth = max(max_depth, curr_depth) # no need to check if depth decreases
    else:
        Q = Q[1:] # leaf: won't revisit, will go to peer, if any, so don't change depth
        print 'no children for', n

return max_depth

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多