Как отслеживать глубину в первом поиске?

У меня есть дерево в качестве входных данных для первого поиска ширины, и я хочу знать, как алгоритм развивается, на каком уровне он находится?

# 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')

В качестве входного графика берется дерево, я хочу, чтобы на каждой итерации он должен был распечатать текущий уровень, который обрабатывается.

Ответ 1

Вам не нужно использовать дополнительную очередь или выполнять сложные вычисления для достижения того, что вы хотите сделать. Эта идея очень проста.

Это не использует дополнительное пространство, отличное от очереди, используемой для 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);
     }

Ответ 2

Поддерживать очередь, хранящую глубину соответствующего node в очереди 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.

Ответ 3

Попробуйте взглянуть на этот пост. Он отслеживает глубину с помощью переменной currentDepth

fooobar.com/questions/437649/...

Для вашей реализации следите за левым большинством node и переменной для глубины. Всякий раз, когда слева больше node выталкивается из очереди, вы знаете, что вы нажмете новый уровень и увеличиваете глубину.

Итак, ваш корень leftMostNode на уровне 0. Тогда самый левый из них - leftMostNode. Как только вы ударите его, он станет уровнем 1. Левое большинство этого node - это следующий leftMostNode и так далее.

Ответ 4

Если ваше дерево идеально сбалансировано (т.е. Каждый узел имеет одинаковое количество дочерних элементов), то здесь действительно простое элегантное решение с 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 учетом d, на самом деле мы хотим решить для d учетом N Например, скажем, мы оцениваем 5-й узел. Чтобы выяснить, на какой глубине находится 5-й узел, мы берем следующее уравнение: 2 ^ d - 1 = 5, а затем просто решаем для d, который является базовой алгеброй:

enter image description here

Если d оказывается чем-то отличным от целого числа, просто округлите (последний узел в строке всегда является целым числом). Имея это в виду, я предлагаю следующий алгоритм для определения глубины любого данного узла в двоичном дереве во время первого обхода ширины:

  1. Пусть visited переменная равна 0.
  2. Каждый раз, когда посещается узел, увеличивается число visited на 1.
  3. Каждый раз, когда visited увеличивается, рассчитайте глубину узла как depth = round_up(log2(visited + 1))

Вы также можете использовать хеш-таблицу для отображения каждого узла на уровень его глубины, хотя это увеличивает сложность пространства до O (n). Вот PHP-реализация этого алгоритма:

<?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);

Какие отпечатки:

    Array
    (
        [1] => Array
            (
                [0] => A
            )

        [2] => Array
            (
                [0] => B
                [1] => C
            )

        [3] => Array
            (
                [0] => D
                [1] => E
                [2] => F
                [3] => G
            )

        [4] => Array
            (
                [0] => H
                [1] => I
                [2] => J
                [3] => K
                [4] => L
                [5] => M
                [6] => N
                [7] => O
            )

    )

Ответ 5

С помощью этого кода 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))

Ответ 6

В Java это будет примерно так. Идея состоит в том, чтобы посмотреть на родителя, чтобы определить глубину.

//Maintain depth for every node based on its parent 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 child :children)
   {
      if (child.isVisited() == false) {
           child.visit(parent);
           depthMap.add(child,depthMap.get(parent)+1);//parent depth + 1
         }

   }

}

Ответ 7

На самом деле, нам не нужна дополнительная очередь для хранения глубины, а также нам не нужно добавлять значение 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++;
}

Ответ 8

Я пишу простой и легкий для чтения код на 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]