이진 트리에 대한 폭 넓은 검색을 재귀 적 으로 구현하려고한다고 가정 해 봅시다 . 어떻게 하시겠습니까?
보조 스택으로 콜 스택 만 사용할 수 있습니까?
이진 트리에 대한 폭 넓은 검색을 재귀 적 으로 구현하려고한다고 가정 해 봅시다 . 어떻게 하시겠습니까?
보조 스택으로 콜 스택 만 사용할 수 있습니까?
답변:
(이것은 일종의 사고 운동이거나 심지어 까다로운 숙제 / 인터뷰 질문이라고 가정하지만 어떤 이유로 힙 공간을 허용하지 않는 기괴한 시나리오를 상상할 수 있다고 가정합니다. 메모리 관리자? 일부 이상한 런타임 / OS 문제?]] 스택에 액세스 할 수있는 동안 ...)
폭 우선 순회는 전통적으로 스택이 아닌 대기열을 사용합니다. 대기열과 스택의 특성은 거의 반대이므로 보조 스택 (큐) 인 호출 스택 (스택이므로 이름)을 사용하려고하면 실패하지 않을 것입니다. 호출 스택에 멍청하게도 말도 안되는 일이 있습니다.
같은 토큰에서, 당신이 구현하려고하는 비 꼬리 재귀의 본질은 본질적으로 알고리즘에 스택을 추가하는 것입니다. 이로 인해 더 이상 이진 트리에서 처음으로 검색하지 않으므로 기존 BFS의 런타임 및 기타 사항이 더 이상 완전히 적용되지 않습니다. 물론 루프를 재귀 호출로 쉽게 바꿀 수 있지만 이는 의미있는 재귀가 아닙니다.
그러나 다른 사람들이 보여 주듯이 BFS의 의미를 따르는 무언가를 어떤 비용으로 구현하는 방법이 있습니다. 비교 비용이 비싸지 만 노드 순회가 저렴한 경우 @Simon Buchan과 마찬가지로 반복 깊이 우선 검색을 실행하여 잎만 처리 할 수 있습니다. 이는 힙에 저장된 증가하는 큐가없고 로컬 깊이 변수 만 있고 트리가 계속해서 반복됨에 따라 호출 스택에서 스택이 계속 누적되는 것을 의미합니다. 있듯이 @Patrick이 언급에 그 너비 우선 탐색은 또한 보조 큐 없이도 사소한 것이되도록 배열에 근거 이진 트리가 통상적 어쨌든 너비 우선 탐색 순서로 저장된다.
배열을 사용하여 이진 트리를 백업하면 다음 노드를 대수적으로 결정할 수 있습니다. i
노드 인 경우 하위 노드는 2i + 1
(왼쪽 노드) 및 2i + 2
(오른쪽 노드) 에서 찾을 수 있습니다 . 노드의 다음 이웃 은의 거듭 제곱이 i + 1
아닌 한i
2
다음은 배열 지원 이진 검색 트리에서 광범위한 첫 번째 검색을 순진하게 구현하기위한 의사 코드입니다. 이것은 고정 크기 배열과 고정 깊이 트리를 가정합니다. 부모없는 노드를보고 관리 할 수 없을 정도로 큰 스택을 만들 수 있습니다.
bintree-bfs(bintree, elt, i)
if (i == LENGTH)
return false
else if (bintree[i] == elt)
return true
else
return bintree-bfs(bintree, elt, i+1)
보조 데이터 구조없이 완전히 재귀 적으로 수행 할 수있는 방법을 찾지 못했습니다. 그러나 큐 Q가 참조로 전달되면 다음과 같은 어리석은 꼬리 재귀 함수를 가질 수 있습니다.
BFS(Q)
{
if (|Q| > 0)
v <- Dequeue(Q)
Traverse(v)
foreach w in children(v)
Enqueue(Q, w)
BFS(Q)
}
다음 방법은 DFS 알고리즘을 사용하여 특정 수준의 모든 노드를 가져 왔습니다. 이는 해당 수준에서 BFS를 수행하는 것과 같습니다. 트리의 깊이를 찾아 모든 레벨에 대해이 작업을 수행하면 결과는 BFS와 동일합니다.
public void PrintLevelNodes(Tree root, int level) {
if (root != null) {
if (level == 0) {
Console.Write(root.Data);
return;
}
PrintLevelNodes(root.Left, level - 1);
PrintLevelNodes(root.Right, level - 1);
}
}
for (int i = 0; i < depth; i++) {
PrintLevelNodes(root, i);
}
나무의 깊이를 찾는 것은 케이크 한 조각입니다.
public int MaxDepth(Tree root) {
if (root == null) {
return 0;
} else {
return Math.Max(MaxDepth(root.Left), MaxDepth(root.Right)) + 1;
}
}
level
는 0이 될 때까지 반환되지 않기 때문입니다.
Java에서 간단한 BFS 및 DFS 재귀 :
스택 / 큐에서 트리의 루트 노드를 푸시 / 제공하고 이러한 함수를 호출하면됩니다.
public static void breadthFirstSearch(Queue queue) {
if (queue.isEmpty())
return;
Node node = (Node) queue.poll();
System.out.println(node + " ");
if (node.right != null)
queue.offer(node.right);
if (node.left != null)
queue.offer(node.left);
breadthFirstSearch(queue);
}
public static void depthFirstSearch(Stack stack) {
if (stack.isEmpty())
return;
Node node = (Node) stack.pop();
System.out.println(node + " ");
if (node.right != null)
stack.push(node.right);
if (node.left != null)
stack.push(node.left);
depthFirstSearch(stack);
}
나는 매우 아름다운 재귀 (기능조차도) 너비 우선 탐색 관련 알고리즘을 발견했습니다. 내 생각은 아니지만이 주제에서 언급해야한다고 생각합니다.
크리스 오카 사키 (Chris Okasaki)는 ICFP 2000의 http://okasaki.blogspot.de/2008/07/breadth-first-numbering-algorithm-in.html 에서 ICFP 2000의 너비 우선 번호 매기기 알고리즘을 3 장의 그림으로 매우 명확하게 설명합니다 .
http://debasishg.blogspot.de/2008/09/breadth-first-numbering-okasakis.html 에서 찾은 Debasish Ghosh의 Scala 구현 은 다음과 같습니다.
trait Tree[+T]
case class Node[+T](data: T, left: Tree[T], right: Tree[T]) extends Tree[T]
case object E extends Tree[Nothing]
def bfsNumForest[T](i: Int, trees: Queue[Tree[T]]): Queue[Tree[Int]] = {
if (trees.isEmpty) Queue.Empty
else {
trees.dequeue match {
case (E, ts) =>
bfsNumForest(i, ts).enqueue[Tree[Int]](E)
case (Node(d, l, r), ts) =>
val q = ts.enqueue(l, r)
val qq = bfsNumForest(i+1, q)
val (bb, qqq) = qq.dequeue
val (aa, tss) = qqq.dequeue
tss.enqueue[org.dg.collection.BFSNumber.Tree[Int]](Node(i, aa, bb))
}
}
}
def bfsNumTree[T](t: Tree[T]): Tree[Int] = {
val q = Queue.Empty.enqueue[Tree[T]](t)
val qq = bfsNumForest(1, q)
qq.dequeue._1
}
바보 같은 방법 :
template<typename T>
struct Node { Node* left; Node* right; T value; };
template<typename T, typename P>
bool searchNodeDepth(Node<T>* node, Node<T>** result, int depth, P pred) {
if (!node) return false;
if (!depth) {
if (pred(node->value)) {
*result = node;
}
return true;
}
--depth;
searchNodeDepth(node->left, result, depth, pred);
if (!*result)
searchNodeDepth(node->right, result, depth, pred);
return true;
}
template<typename T, typename P>
Node<T>* searchNode(Node<T>* node, P pred) {
Node<T>* result = NULL;
int depth = 0;
while (searchNodeDepth(node, &result, depth, pred) && !result)
++depth;
return result;
}
int main()
{
// a c f
// b e
// d
Node<char*>
a = { NULL, NULL, "A" },
c = { NULL, NULL, "C" },
b = { &a, &c, "B" },
f = { NULL, NULL, "F" },
e = { NULL, &f, "E" },
d = { &b, &e, "D" };
Node<char*>* found = searchNode(&d, [](char* value) -> bool {
printf("%s\n", value);
return !strcmp((char*)value, "F");
});
printf("found: %s\n", found->value);
return 0;
}
짧은 스칼라 솔루션 은 다음과 같습니다 .
def bfs(nodes: List[Node]): List[Node] = {
if (nodes.nonEmpty) {
nodes ++ bfs(nodes.flatMap(_.children))
} else {
List.empty
}
}
반환 값을 누산기로 사용하는 아이디어가 적합합니다. 비슷한 방식으로 다른 언어로 구현할 수 있습니다. 재귀 함수 가 노드 목록을 처리하는지 확인하십시오 .
테스트 코드 목록 (@marco 테스트 트리 사용) :
import org.scalatest.FlatSpec
import scala.collection.mutable
class Node(val value: Int) {
private val _children: mutable.ArrayBuffer[Node] = mutable.ArrayBuffer.empty
def add(child: Node): Unit = _children += child
def children = _children.toList
override def toString: String = s"$value"
}
class BfsTestScala extends FlatSpec {
// 1
// / | \
// 2 3 4
// / | | \
// 5 6 7 8
// / | | \
// 9 10 11 12
def tree(): Node = {
val root = new Node(1)
root.add(new Node(2))
root.add(new Node(3))
root.add(new Node(4))
root.children(0).add(new Node(5))
root.children(0).add(new Node(6))
root.children(2).add(new Node(7))
root.children(2).add(new Node(8))
root.children(0).children(0).add(new Node(9))
root.children(0).children(0).add(new Node(10))
root.children(2).children(0).add(new Node(11))
root.children(2).children(0).add(new Node(12))
root
}
def bfs(nodes: List[Node]): List[Node] = {
if (nodes.nonEmpty) {
nodes ++ bfs(nodes.flatMap(_.children))
} else {
List.empty
}
}
"BFS" should "work" in {
println(bfs(List(tree())))
}
}
산출:
List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)
파이썬 구현은 다음과 같습니다.
graph = {'A': ['B', 'C'],
'B': ['C', 'D'],
'C': ['D'],
'D': ['C'],
'E': ['F'],
'F': ['C']}
def bfs(paths, goal):
if not paths:
raise StopIteration
new_paths = []
for path in paths:
if path[-1] == goal:
yield path
last = path[-1]
for neighbor in graph[last]:
if neighbor not in path:
new_paths.append(path + [neighbor])
yield from bfs(new_paths, goal)
for path in bfs([['A']], 'D'):
print(path)
다음은 재귀 BFS의 Scala 2.11.4 구현입니다. 간결성을 위해 테일 콜 최적화를 희생했지만 TCOd 버전은 매우 유사합니다. @snv 의 게시물 도 참조하십시오 .
import scala.collection.immutable.Queue
object RecursiveBfs {
def bfs[A](tree: Tree[A], target: A): Boolean = {
bfs(Queue(tree), target)
}
private def bfs[A](forest: Queue[Tree[A]], target: A): Boolean = {
forest.dequeueOption exists {
case (E, tail) => bfs(tail, target)
case (Node(value, _, _), _) if value == target => true
case (Node(_, l, r), tail) => bfs(tail.enqueue(List(l, r)), target)
}
}
sealed trait Tree[+A]
case class Node[+A](data: A, left: Tree[A], right: Tree[A]) extends Tree[A]
case object E extends Tree[Nothing]
}
다음은 Haskell을 사용하여 나에게 자연스러운 것 같습니다. 트리의 레벨을 재귀 적으로 반복하십시오 (여기서는 트리를 통한 경로를 보여주기 위해 큰 순서의 문자열로 이름을 수집합니다).
data Node = Node {name :: String, children :: [Node]}
aTree = Node "r" [Node "c1" [Node "gc1" [Node "ggc1" []], Node "gc2" []] , Node "c2" [Node "gc3" []], Node "c3" [] ]
breadthFirstOrder x = levelRecurser [x]
where levelRecurser level = if length level == 0
then ""
else concat [name node ++ " " | node <- level] ++ levelRecurser (concat [children node | node <- level])
다음은 사이클이없는 그래프에서 작동하는 BFS 재귀 순회 Python 구현입니다.
def bfs_recursive(level):
'''
@params level: List<Node> containing the node for a specific level.
'''
next_level = []
for node in level:
print(node.value)
for child_node in node.adjency_list:
next_level.append(child_node)
if len(next_level) != 0:
bfs_recursive(next_level)
class Node:
def __init__(self, value):
self.value = value
self.adjency_list = []
언어가 생성기와 같은 것을 지원하면 bfs를 동시 재귀 적으로 수행 할 수 있다는 점에서 센트를 최고 답변 에 추가 하고 싶습니다.
우선 @Tanzelax의 답변은 다음과 같습니다.
폭 우선 순회는 전통적으로 스택이 아닌 대기열을 사용합니다. 대기열과 스택의 특성은 거의 반대이므로 보조 저장소 (대기열) 인 호출 스택 (스택, 즉 이름)을 사용하려고하면 거의 실패하게됩니다.
실제로 일반 함수 호출 스택은 일반 스택처럼 작동하지 않습니다. 그러나 생성기 함수는 함수 실행을 일시 중지하므로 노드의 더 깊은 하위 항목을 탐색하지 않고도 다음 수준의 노드 하위 항목을 생성 할 수 있습니다.
다음 코드는 파이썬에서 재귀 bfs입니다.
def bfs(root):
yield root
for n in bfs(root):
for c in n.children:
yield c
직관은 다음과 같습니다.
BFS 순서로 출력되는 힙 순회를 구현해야했습니다. 실제로 BFS는 아니지만 동일한 작업을 수행합니다.
private void getNodeValue(Node node, int index, int[] array) {
array[index] = node.value;
index = (index*2)+1;
Node left = node.leftNode;
if (left!=null) getNodeValue(left,index,array);
Node right = node.rightNode;
if (right!=null) getNodeValue(right,index+1,array);
}
public int[] getHeap() {
int[] nodes = new int[size];
getNodeValue(root,0,nodes);
return nodes;
}
v를 시작 정점으로하자
G를 문제의 그래프로하자
다음은 대기열을 사용하지 않는 의사 코드입니다
Initially label v as visited as you start from v
BFS(G,v)
for all adjacent vertices w of v in G:
if vertex w is not visited:
label w as visited
for all adjacent vertices w of v in G:
recursively call BFS(G,w)
바이너리 (또는 n-ary) 트리의 BFS는 다음과 같이 큐없이 재귀 적으로 수행 할 수 있습니다 (여기서는 Java).
public class BreathFirst {
static class Node {
Node(int value) {
this(value, 0);
}
Node(int value, int nChildren) {
this.value = value;
this.children = new Node[nChildren];
}
int value;
Node[] children;
}
static void breathFirst(Node root, Consumer<? super Node> printer) {
boolean keepGoing = true;
for (int level = 0; keepGoing; level++) {
keepGoing = breathFirst(root, printer, level);
}
}
static boolean breathFirst(Node node, Consumer<? super Node> printer, int depth) {
if (depth < 0 || node == null) return false;
if (depth == 0) {
printer.accept(node);
return true;
}
boolean any = false;
for (final Node child : node.children) {
any |= breathFirst(child, printer, depth - 1);
}
return any;
}
}
오름차순으로 순회 인쇄 번호 1-12의 예는 다음과 같습니다.
public static void main(String... args) {
// 1
// / | \
// 2 3 4
// / | | \
// 5 6 7 8
// / | | \
// 9 10 11 12
Node root = new Node(1, 3);
root.children[0] = new Node(2, 2);
root.children[1] = new Node(3);
root.children[2] = new Node(4, 2);
root.children[0].children[0] = new Node(5, 2);
root.children[0].children[1] = new Node(6);
root.children[2].children[0] = new Node(7, 2);
root.children[2].children[1] = new Node(8);
root.children[0].children[0].children[0] = new Node(9);
root.children[0].children[0].children[1] = new Node(10);
root.children[2].children[0].children[0] = new Node(11);
root.children[2].children[0].children[1] = new Node(12);
breathFirst(root, n -> System.out.println(n.value));
}
#include <bits/stdc++.h>
using namespace std;
#define Max 1000
vector <int> adj[Max];
bool visited[Max];
void bfs_recursion_utils(queue<int>& Q) {
while(!Q.empty()) {
int u = Q.front();
visited[u] = true;
cout << u << endl;
Q.pop();
for(int i = 0; i < (int)adj[u].size(); ++i) {
int v = adj[u][i];
if(!visited[v])
Q.push(v), visited[v] = true;
}
bfs_recursion_utils(Q);
}
}
void bfs_recursion(int source, queue <int>& Q) {
memset(visited, false, sizeof visited);
Q.push(source);
bfs_recursion_utils(Q);
}
int main(void) {
queue <int> Q;
adj[1].push_back(2);
adj[1].push_back(3);
adj[1].push_back(4);
adj[2].push_back(5);
adj[2].push_back(6);
adj[3].push_back(7);
bfs_recursion(1, Q);
return 0;
}
다음은 Breadth First Traversal을 Depth First 재귀로 가짜로 만드는 JavaScript 구현입니다. 배열 내부, 해시 내부의 각 깊이에서 노드 값을 저장하고 있습니다. 레벨이 이미 존재하는 경우 (충돌이 있음) 해당 레벨의 배열로 푸시합니다. 레벨이 숫자이고 배열 색인으로 사용될 수 있으므로 JavaScript 객체 대신 배열을 사용할 수 있습니다. 노드, 값을 반환하거나 연결된 목록으로 변환하거나 원하는 것을 반환 할 수 있습니다. 간단하게하기 위해 값을 반환하고 있습니다.
BinarySearchTree.prototype.breadthFirstRec = function() {
var levels = {};
var traverse = function(current, depth) {
if (!current) return null;
if (!levels[depth]) levels[depth] = [current.value];
else levels[depth].push(current.value);
traverse(current.left, depth + 1);
traverse(current.right, depth + 1);
};
traverse(this.root, 0);
return levels;
};
var bst = new BinarySearchTree();
bst.add(20, 22, 8, 4, 12, 10, 14, 24);
console.log('Recursive Breadth First: ', bst.breadthFirstRec());
/*Recursive Breadth First:
{ '0': [ 20 ],
'1': [ 8, 22 ],
'2': [ 4, 12, 24 ],
'3': [ 10, 14 ] } */
다음은 반복 접근 방식을 사용하는 실제 너비 우선 탐색의 예입니다.
BinarySearchTree.prototype.breadthFirst = function() {
var result = '',
queue = [],
current = this.root;
if (!current) return null;
queue.push(current);
while (current = queue.shift()) {
result += current.value + ' ';
current.left && queue.push(current.left);
current.right && queue.push(current.right);
}
return result;
};
console.log('Breadth First: ', bst.breadthFirst());
//Breadth First: 20 8 22 4 12 24 10 14
다음은 루프와 큐를 사용하지 않고 양방향 그래프의 너비 우선 탐색을 완전히 재귀 적으로 구현하는 코드입니다.
public class Graph
{
public int V;
public LinkedList<Integer> adj[];
Graph(int v)
{
V = v;
adj = new LinkedList[v];
for (int i=0; i<v; ++i)
adj[i] = new LinkedList<>();
}
void addEdge(int v,int w)
{
adj[v].add(w);
adj[w].add(v);
}
public LinkedList<Integer> getAdjVerted(int vertex)
{
return adj[vertex];
}
public String toString()
{
String s = "";
for (int i=0;i<adj.length;i++)
{
s = s +"\n"+i +"-->"+ adj[i] ;
}
return s;
}
}
//BFS IMPLEMENTATION
public static void recursiveBFS(Graph graph, int vertex,boolean visited[], boolean isAdjPrinted[])
{
if (!visited[vertex])
{
System.out.print(vertex +" ");
visited[vertex] = true;
}
if(!isAdjPrinted[vertex])
{
isAdjPrinted[vertex] = true;
List<Integer> adjList = graph.getAdjVerted(vertex);
printAdjecent(graph, adjList, visited, 0,isAdjPrinted);
}
}
public static void recursiveBFS(Graph graph, List<Integer> vertexList, boolean visited[], int i, boolean isAdjPrinted[])
{
if (i < vertexList.size())
{
recursiveBFS(graph, vertexList.get(i), visited, isAdjPrinted);
recursiveBFS(graph, vertexList, visited, i+1, isAdjPrinted);
}
}
public static void printAdjecent(Graph graph, List<Integer> list, boolean visited[], int i, boolean isAdjPrinted[])
{
if (i < list.size())
{
if (!visited[list.get(i)])
{
System.out.print(list.get(i)+" ");
visited[list.get(i)] = true;
}
printAdjecent(graph, list, visited, i+1, isAdjPrinted);
}
else
{
recursiveBFS(graph, list, visited, 0, isAdjPrinted);
}
}
이진 트리에 대한 재귀 너비 우선 검색 알고리즘의 C # 구현.
IDictionary<string, string[]> graph = new Dictionary<string, string[]> {
{"A", new [] {"B", "C"}},
{"B", new [] {"D", "E"}},
{"C", new [] {"F", "G"}},
{"E", new [] {"H"}}
};
void Main()
{
var pathFound = BreadthFirstSearch("A", "H", new string[0]);
Console.WriteLine(pathFound); // [A, B, E, H]
var pathNotFound = BreadthFirstSearch("A", "Z", new string[0]);
Console.WriteLine(pathNotFound); // []
}
IEnumerable<string> BreadthFirstSearch(string start, string end, IEnumerable<string> path)
{
if (start == end)
{
return path.Concat(new[] { end });
}
if (!graph.ContainsKey(start)) { return new string[0]; }
return graph[start].SelectMany(letter => BreadthFirstSearch(letter, end, path.Concat(new[] { start })));
}
알고리즘이 이진 트리뿐 아니라 다른 노드와 동일한 노드를 가리키는 둘 이상의 노드를 가질 수있는 그래프를 사용하려면 이미 방문한 노드 목록을 보유하여 자체 순환을 피해야합니다. 구현은 다음과 같습니다.
IDictionary<string, string[]> graph = new Dictionary<string, string[]> {
{"A", new [] {"B", "C"}},
{"B", new [] {"D", "E"}},
{"C", new [] {"F", "G", "E"}},
{"E", new [] {"H"}}
};
void Main()
{
var pathFound = BreadthFirstSearch("A", "H", new string[0], new List<string>());
Console.WriteLine(pathFound); // [A, B, E, H]
var pathNotFound = BreadthFirstSearch("A", "Z", new string[0], new List<string>());
Console.WriteLine(pathNotFound); // []
}
IEnumerable<string> BreadthFirstSearch(string start, string end, IEnumerable<string> path, IList<string> visited)
{
if (start == end)
{
return path.Concat(new[] { end });
}
if (!graph.ContainsKey(start)) { return new string[0]; }
return graph[start].Aggregate(new string[0], (acc, letter) =>
{
if (visited.Contains(letter))
{
return acc;
}
visited.Add(letter);
var result = BreadthFirstSearch(letter, end, path.Concat(new[] { start }), visited);
return acc.Concat(result).ToArray();
});
}
c ++를 사용하여 공동 및 분리 그래프에서도 작동하는 프로그램을 만들었습니다.
#include <queue>
#include "iostream"
#include "vector"
#include "queue"
using namespace std;
struct Edge {
int source,destination;
};
class Graph{
int V;
vector<vector<int>> adjList;
public:
Graph(vector<Edge> edges,int V){
this->V = V;
adjList.resize(V);
for(auto i : edges){
adjList[i.source].push_back(i.destination);
// adjList[i.destination].push_back(i.source);
}
}
void BFSRecursivelyJoinandDisjointtGraphUtil(vector<bool> &discovered, queue<int> &q);
void BFSRecursivelyJointandDisjointGraph(int s);
void printGraph();
};
void Graph :: printGraph()
{
for (int i = 0; i < this->adjList.size(); i++)
{
cout << i << " -- ";
for (int v : this->adjList[i])
cout <<"->"<< v << " ";
cout << endl;
}
}
void Graph ::BFSRecursivelyJoinandDisjointtGraphUtil(vector<bool> &discovered, queue<int> &q) {
if (q.empty())
return;
int v = q.front();
q.pop();
cout << v <<" ";
for (int u : this->adjList[v])
{
if (!discovered[u])
{
discovered[u] = true;
q.push(u);
}
}
BFSRecursivelyJoinandDisjointtGraphUtil(discovered, q);
}
void Graph ::BFSRecursivelyJointandDisjointGraph(int s) {
vector<bool> discovered(V, false);
queue<int> q;
for (int i = s; i < V; i++) {
if (discovered[i] == false)
{
discovered[i] = true;
q.push(i);
BFSRecursivelyJoinandDisjointtGraphUtil(discovered, q);
}
}
}
int main()
{
vector<Edge> edges =
{
{0, 1}, {0, 2}, {1, 2}, {2, 0}, {2,3},{3,3}
};
int V = 4;
Graph graph(edges, V);
// graph.printGraph();
graph.BFSRecursivelyJointandDisjointGraph(2);
cout << "\n";
edges = {
{0,4},{1,2},{1,3},{1,4},{2,3},{3,4}
};
Graph graph2(edges,5);
graph2.BFSRecursivelyJointandDisjointGraph(0);
return 0;
}