幅優先検索への入力としてツリーがあり、アルゴリズムがどのレベルで進行するかを知りたいですか?
# 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を使用します。これは、各反復で、処理中の現在のレベルを出力する必要があるということです。
やりたいことを達成するために、余分なキューを使用したり、複雑な計算をしたりする必要はありません。このアイデアは非常に簡単です。
これは、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);
}
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による回答投稿が必要になる場合があります。
ツリーが完全にバランスが取れている(つまり、各ノードが同じ数の子を持つ)場合、実際にはO(1)時間の複雑さとO(1) space complex。これが役立つと思う主なユースケースは、他のツリーサイズに簡単に適応できますが、バイナリツリーを走査することです。
ここで重要なことは、バイナリツリーの各レベルには、前のレベルに比べてノードの量が正確に2倍含まれていることです。これにより、ツリーの深さが指定されたツリーのノードの総数を計算できます。たとえば、次のツリーを検討してください。
このツリーの深さは、合計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
、これは基本的な代数です:
d
が整数以外の場合、切り上げるだけです(行の最後のノードは常に整数です)。それをすべて念頭に置いて、幅優先走査中にバイナリツリー内の任意のノードの深さを識別するために、次のアルゴリズムを提案します。
visited
を0にします。visited
を1ずつ増やします。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
)
)
この投稿をご覧ください。変数currentDepth
を使用して深さを追跡します
https://stackoverflow.com/a/16923440/3114945
実装のために、左端のノードと深さの変数を追跡します。左端のノードがキューからポップされるたびに、新しいレベルに到達し、深さをインクリメントすることがわかります。
したがって、ルートはレベル0のleftMostNode
です。その後、左端の子はleftMostNode
です。ヒットするとすぐにレベル1になります。このノードの左端の子は次のleftMostNode
などです。
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))
実際、深さを格納するために余分なキューは必要ありません。また、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++;
}
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
}
}
}
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]