如何在广度优先搜索中跟踪深度?

时间:2015-07-06 13:50:53

标签: algorithm data-structures graph graph-algorithm

我有一棵树作为广度优先搜索的输入,我想知道算法在哪个级别进展?

# Breadth First Search Implementation
graph = { 
    'A':['B','C','D'],
    'B':['A'],
    'C':['A','E','F'],
    'D':['A','G','H'],
    'E':['C'],
    'F':['C'],
    'G':['D'],
    'H':['D']
    }


def breadth_first_search(graph,source):
    """
    This function is the Implementation of the breadth_first_search program
    """
    # Mark each node as not visited
    mark = {}
    for item in graph.keys():
        mark[item] = 0

    queue, output = [],[]

    # Initialize an empty queue with the source node and mark it as explored
    queue.append(source)
    mark[source] = 1
    output.append(source)

    # while queue is not empty
    while queue:
        # remove the first element of the queue and call it vertex
        vertex = queue[0]
        queue.pop(0)
        # for each edge from the vertex do the following
        for vrtx in graph[vertex]:
            # If the vertex is unexplored
            if mark[vrtx] == 0:
                queue.append(vrtx)  # mark it as explored
                mark[vrtx] = 1      # and append it to the queue
                output.append(vrtx) # fill the output vector
    return output

print breadth_first_search(graph, 'A')

它将树作为输入图,我想要的是,在每次迭代时它应该打印出正在处理的当前级别。

10 个答案:

答案 0 :(得分:31)

您不需要使用额外的队列或进行任何复杂的计算来实现您想要的目标。这个想法很简单。

除了用于BFS的队列之外,这不会使用任何额外的空间。

我要使用的想法是在每个级别的末尾添加null。因此,您遇到的空值数+1是您所处的深度。 (当然在终止后它只是level)。

     int level = 0;
     Queue <Node> queue = new LinkedList<>();
     queue.add(root);
     queue.add(null);
     while(!queue.isEmpty()){
          Node temp = queue.poll();
          if(temp == null){
              level++;
              queue.add(null);
              if(queue.peek() == null) break;// You are encountering two consecutive `nulls` means, you visited all the nodes.
              else continue;
          }
          if(temp.right != null)
              queue.add(temp.right);
          if(temp.left != null)
              queue.add(temp.left);
     }

答案 1 :(得分:4)

维护一个队列,在BFS队列中存储相应节点的深度。您的信息的示例代码:

queue bfsQueue, depthQueue;
bfsQueue.push(firstNode);
depthQueue.push(0);
while (!bfsQueue.empty()) {
    f = bfsQueue.front();
    depth = depthQueue.front();
    bfsQueue.pop(), depthQueue.pop();
    for (every node adjacent to f) {
        bfsQueue.push(node), depthQueue.push(depth+1);
    } 
}

这种方法简单而幼稚,对于O(1)额外空间,您可能需要@stolen_leaves的答案帖。

答案 2 :(得分:3)

试试看这篇文章。它使用变量currentDepth

跟踪深度

https://stackoverflow.com/a/16923440/3114945

对于您的实施,跟踪最左侧节点和深度变量。每当从队列中弹出最左边的节点时,你知道你达到了一个新的水平并且你增加了深度。

所以,你的根是0级的leftMostNode。那么最左边的孩子就是leftMostNode。一旦你点击它,它就变为1级。这个节点的最左边的孩子是下一个leftMostNode,依此类推。

答案 3 :(得分:3)

如果您的树完全平衡(即每个节点具有相同数量的子节点),实际上这里有一个简单,优雅的解决方案,具有O(1)时间复杂度和O(1)空间复杂度。我发现这个有用的主要用例是遍历二叉树,尽管它可以很容易地适应其他树的大小。

这里要认识到的关键是二叉树的每个级别包含的节点数量与前一级别相比只有两倍。这允许我们在给定树的深度的情况下计算任何树中的节点总数。例如,请考虑以下树:

enter image description here

此树的总深度为3和7个节点。我们不需要计算节点数来解决这个问题。我们可以在O(1)时间内使用公式计算:2 ^ d - 1 = N,其中d是深度,N是节点的总数。 (在三元树中,这是3 ^ d - 1 = N,并且在树中,每个节点具有K个子节点,这是K ^ d - 1 = N)。所以在这种情况下,2 ^ 3 - 1 = 7。

要在进行广度优先搜索时跟踪深度,我们只需要反转此计算。鉴于以上公式允许我们解决给定N的{​​{1}},我们实际上想要求d给定d。例如,假设我们正在评估第5个节点。为了确定第5个节点的深度,我们采用以下等式:2 ^ d - 1 = 5,然后简单求解N ,这是基本代数:

enter image description here

如果d不是一个整数,那么只是向上舍入(一行中的最后一个节点总是一个整数)。考虑到这一点,我建议使用以下算法在广度优先遍历期间识别二叉树中任何给定节点的深度:

  1. 让变量d等于0。
  2. 每次访问节点时,将visited递增1。
  3. 每次visited递增时,将节点的深度计算为visited
  4. 您还可以使用哈希表将每个节点映射到其深度级别,但这确实会将空间复杂度增加到O(n)。这是该算法的PHP实现:

    depth = round_up(log2(visited + 1))

    打印哪些:

    <?php
    $tree = [
        ['A', [1,2]],
        ['B', [3,4]],
        ['C', [5,6]],
        ['D', [7,8]],
        ['E', [9,10]],
        ['F', [11,12]],
        ['G', [13,14]],
        ['H', []],
        ['I', []],
        ['J', []],
        ['K', []],
        ['L', []],
        ['M', []],
        ['N', []],
        ['O', []],
    ];
    
    function bfs($tree) {
        $queue = new SplQueue();
        $queue->enqueue($tree[0]);
        $visited = 0;
        $depth = 0;
        $result = [];
    
        while ($queue->count()) {
    
            $visited++;
            $node = $queue->dequeue();
            $depth = ceil(log($visited+1, 2));
            $result[$depth][] = $node[0];
    
    
            if (!empty($node[1])) {
                foreach ($node[1] as $child) {
                    $queue->enqueue($tree[$child]);
                }
            }
        }
        print_r($result);
    }
    
    bfs($tree);
    

答案 4 :(得分:1)

使用此Python代码,只有在队列中遇到新深度的节点之后,才可以通过增加深度来保持每个节点距根的深度。

    queue = deque()
    marked = set()
    marked.add(root)
    queue.append((root,0))

    depth = 0
    while queue:
        r,d = queue.popleft()
        if d > depth: # increase depth only when you encounter the first node in the next depth               
            depth += 1
        for node in edges[r]:
            if node not in marked:
                marked.add(node)
                queue.append((node,depth+1))

答案 5 :(得分:1)

在Java中将是这样。 想法是看父母决定深度。

//Maintain depth for every node based on its parent's depth
Map<Character,Integer> depthMap=new HashMap<>();    

queue.add('A');
depthMap.add('A',0); //this is where you start your search

while(!queue.isEmpty())
{
   Character parent=queue.remove();
   List<Character> children=adjList.get(parent);
   for(Character c:children)
   {
      depthMap.add(c,depthMap.get(parent)+1);//parent's depth + 1

   }

}

答案 6 :(得分:1)

实际上,我们不需要额外的队列来存储深度,也不需要添加null来告诉它是否是当前级别的结尾。我们只需要当前级别有多少个节点,就可以处理同一级别的所有节点,完成后将级别增加1。

int level = 0;
Queue<Node> queue = new LinkedList<>();
queue.add(root);
while(!queue.isEmpty()){
    int level_size = queue.size();
    while (level_size--) {
        Node temp = queue.poll();
        if (temp.right != null) queue.add(temp.right);
        if (temp.left != null) queue.add(temp.left);
    }    
    level++;
}

答案 7 :(得分:0)

我用python写了一个简单易读的代码。

class TreeNode:
    def __init__(self, x):
        self.val = x
        self.left = None
        self.right = None

class Solution:
    def dfs(self, root):
        assert root is not None
        queue = [root]
        level = 0
        while queue:
            print(level, [n.val for n in queue if n is not None])
            mark = len(queue)
            for i in range(mark):
                n = queue[i]
                if n.left is not None:
                    queue.append(n.left)
                if n.right is not None:
                    queue.append(n.right)
            queue = queue[mark:]
            level += 1

用法

# [3,9,20,null,null,15,7]
n3 = TreeNode(3)
n9 = TreeNode(9)
n20 = TreeNode(20)
n15 = TreeNode(15)
n7 = TreeNode(7)
n3.left = n9
n3.right = n20
n20.left = n15
n20.right = n7
DFS().dfs(n3)

结果

0 [3]
1 [9, 20]
2 [15, 7]

答案 8 :(得分:0)

在浏览图形时,使用字典来跟踪每个节点的水平(距起点的距离)。

Python示例:

from collections import deque

def bfs(graph, start):
    queue = deque([start])
    levels = {start: 0}
    while queue:
        vertex = queue.popleft()
        for neighbour in graph[vertex]:
            if neighbour in levels:
                continue
            queue.append(neighbour)
            levels[neighbour] = levels[vertex] + 1
    return levels

答案 9 :(得分:0)

设置变量cnt并将其初始化为队列cnt=queue.size()的大小,现在每次弹出时递减cnt。当cnt变为0时,增加BFS的深度,然后再次设置cnt=queue.size()