web-dev-qa-db-ja.com

幅優先検索で深さを追跡する方法は?

幅優先検索への入力としてツリーがあり、アルゴリズムがどのレベルで進行するかを知りたいですか?

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

入力グラフとしてtreeを使用します。これは、各反復で、処理中の現在のレベルを出力する必要があるということです。

16
functioncall

やりたいことを達成するために、余分なキューを使用したり、複雑な計算をしたりする必要はありません。このアイデアは非常に簡単です。

これは、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);
     }
31
Karthik

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による回答投稿が必要になる場合があります。

5
matrixit

ツリーが完全にバランスが取れている(つまり、各ノードが同じ数の子を持つ)場合、実際にはO(1)時間の複雑さとO(1) space complex。これが役立つと思う主なユースケースは、他のツリーサイズに簡単に適応できますが、バイナリツリーを走査することです。

ここで重要なことは、バイナリツリーの各レベルには、前のレベルに比べてノードの量が正確に2倍含まれていることです。これにより、ツリーの深さが指定されたツリーのノードの総数を計算できます。たとえば、次のツリーを検討してください。

enter image description here

このツリーの深さは、合計3および7ノードです。ただし、これを把握するためにノードの数を数える必要はありません。これをO(1)時間で計算できます。式2 ^ d-1 = N、ここでdは深さ、Nはノードの合計数(3進ツリーでは、これは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
            )

    )
3
Matt Korostoff

この投稿をご覧ください。変数currentDepthを使用して深さを追跡します

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

実装のために、左端のノードと深さの変数を追跡します。左端のノードがキューからポップされるたびに、新しいレベルに到達し、深さをインクリメントすることがわかります。

したがって、ルートはレベル0のleftMostNodeです。その後、左端の子はleftMostNodeです。ヒットするとすぐにレベル1になります。このノードの左端の子は次のleftMostNodeなどです。

3
stolen_leaves

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))
2
MROB

実際、深さを格納するために余分なキューは必要ありません。また、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++;
}
1
KDD

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 child :children)
   {
      if (child.isVisited() == false) {
           child.visit(parent);
           depthMap.add(child,depthMap.get(parent)+1);//parent's depth + 1
         }

   }

}
1
developer747

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]
0
s-han.lee