너비 우선 검색을 재귀 적으로 수행


152

이진 트리에 대한 폭 넓은 검색을 재귀 적 으로 구현하려고한다고 가정 해 봅시다 . 어떻게 하시겠습니까?

보조 스택으로 콜 스택 만 사용할 수 있습니까?


14
아주 좋은 질문입니다. 이것은 전혀 간단하지 않습니다. 기본적으로 스택 만 사용하여 BFS를 구현하도록 요청합니다.
sisis

4
스택만으로 재귀 적으로? 이것은 내 머리를 아프게합니다.
케빈 프리드 하임

11
나는 보통 재귀 동작을 제거하기 위해 스택을 사용합니다
Newtopian

Max 힙에서 BFS를 사용하는 경우 아래 제공된 솔루션이 제대로 작동하는지 궁금합니다. 이견있는 사람 ?
Jay D

답변:


123

(이것은 일종의 사고 운동이거나 심지어 까다로운 숙제 / 인터뷰 질문이라고 가정하지만 어떤 이유로 힙 공간을 허용하지 않는 기괴한 시나리오를 상상할 수 있다고 가정합니다. 메모리 관리자? 일부 이상한 런타임 / OS 문제?]] 스택에 액세스 할 수있는 동안 ...)

폭 우선 순회는 전통적으로 스택이 아닌 대기열을 사용합니다. 대기열과 스택의 특성은 거의 반대이므로 보조 스택 (큐) 인 호출 스택 (스택이므로 이름)을 사용하려고하면 실패하지 않을 것입니다. 호출 스택에 멍청하게도 말도 안되는 일이 있습니다.

같은 토큰에서, 당신이 구현하려고하는 비 꼬리 재귀의 본질은 본질적으로 알고리즘에 스택을 추가하는 것입니다. 이로 인해 더 이상 이진 트리에서 처음으로 검색하지 않으므로 기존 BFS의 런타임 및 기타 사항이 더 이상 완전히 적용되지 않습니다. 물론 루프를 재귀 호출로 쉽게 바꿀 수 있지만 이는 의미있는 재귀가 아닙니다.

그러나 다른 사람들이 보여 주듯이 BFS의 의미를 따르는 무언가를 어떤 비용으로 구현하는 방법이 있습니다. 비교 비용이 비싸지 만 노드 순회가 저렴한 경우 @Simon Buchan과 마찬가지로 반복 깊이 우선 검색을 실행하여 잎만 처리 할 수 ​​있습니다. 이는 힙에 저장된 증가하는 큐가없고 로컬 깊이 변수 만 있고 트리가 계속해서 반복됨에 따라 호출 스택에서 스택이 계속 누적되는 것을 의미합니다. 있듯이 @Patrick이 언급에 그 너비 우선 탐색은 또한 보조 큐 없이도 사소한 것이되도록 배열에 근거 이진 트리가 통상적 어쨌든 너비 우선 탐색 순서로 저장된다.


10
이것은 실제로 단지 생각 운동입니다. 나는 당신이 실제로 이것을하고 싶은 상황을 상상할 수 없습니다. 신중하게 답변 해 주셔서 감사합니다!
Nate

2
" 하지만 어떤 이유로 든 힙 공간이 허용되지 않는 기괴한 시나리오 를 상상할 수 있다고 생각합니다.": Dunno, 스택 (읽기 전용 메모리 공간과 함께) 만 사용할 수있는 임베디드 환경을 상상할 수 있습니다. 실제로 프로그램이 무엇을할지 정확히 알고 있다면 일반적으로 임베디드 소프트웨어의 경우 힙을 사용하지 않고 소프트웨어를 작성하는 것이 매우 쉽고 효율적입니다. 그래서 그것은 "기괴한"것이 아닙니다. 특이하지는 않지만 기괴하지는 않습니다.
토마스

귀하의 답변 에이 기사 ( ibm.com/developerworks/aix/library/au-aix-stack-tree-traversal )에 대한 참조가 포함되어 있다고 생각합니다 . 답의 두 번째 부분에서 작성한 것에 대한 구현을 보여줍니다
incud

25

배열을 사용하여 이진 트리를 백업하면 다음 노드를 대수적으로 결정할 수 있습니다. i노드 인 경우 하위 노드는 2i + 1(왼쪽 노드) 및 2i + 2(오른쪽 노드) 에서 찾을 수 있습니다 . 노드의 다음 이웃 은의 거듭 제곱이 i + 1아닌 한i2

다음은 배열 지원 이진 검색 트리에서 광범위한 첫 번째 검색을 순진하게 구현하기위한 의사 코드입니다. 이것은 고정 크기 배열과 고정 깊이 트리를 가정합니다. 부모없는 노드를보고 관리 할 수 ​​없을 정도로 큰 스택을 만들 수 있습니다.

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)        

1
좋은. 우리가 이진 트리를 다루고 있다는 사실을 간과했습니다. 인덱스는 DFS를 사용하여 할당 할 수 있습니다. BTW, 첫 번째 경우에 false를 잊어 버렸습니다.
sisis

큐잉 방법을 선호한다고 생각합니다 .P. 반환 거짓을 추가했습니다.
Patrick McMurchie 1

1
영리한. 노드를 배열에 저장하고 대수적으로 참조한다는 아이디어는 나에게 발생하지 않았습니다.
Nate

19

보조 데이터 구조없이 완전히 재귀 적으로 수행 할 수있는 방법을 찾지 못했습니다. 그러나 큐 Q가 참조로 전달되면 다음과 같은 어리석은 꼬리 재귀 함수를 가질 수 있습니다.

BFS(Q)
{
  if (|Q| > 0)
     v <- Dequeue(Q)
     Traverse(v)
     foreach w in children(v)
        Enqueue(Q, w)    

     BFS(Q)
}

6
깨끗하고 올바른 기능을 위해 재귀를 추가하는 것은 부 자연스러운 방법입니다.
Mysterion

완전히 동의하지 않습니다. 더 자연스럽고 유용합니다. 층을 통과 할 때이 방법을 확장하여 작업 상태를 전달할 수 있습니다
Tom Golden

15

다음 방법은 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;
    }
}

코드 형식에 좀 더주의를 기울이십시오. 나는 약간의 변화를했다.
Micha

그러나 잠깐만 .. 이것이 BFS가 아닌 DFS입니까? PrintLevelNodes level는 0이 될 때까지 반환되지 않기 때문입니다.
Herrington Darkholme

1
@HerringtonDarkholme, 맞습니다. DFS 검색을 수행하지만 레벨에 대해 BFS를 수행 한 것처럼 값을 출력합니다. 지적 해 주셔서 감사합니다.
Sanj

1
@Sanjay, 이것은 실제로 DFS 순서로 노드에서 어떤 동작을 수행 할 수 있는지에 대한 훌륭한 데모입니다. 반드시 DFS 순서로 노드를 실제로 "만지는"방법 일 필요는 없지만 DFS 순서로 노드에 대해 재귀 적으로 "작동"하여 값을 인쇄 할 수 있습니다.
bunkerdive

8

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

4
스택에 이미 암시 적 스택이 있기 때문에 스택을 DFS의 매개 변수로 전달하는 것은 약간 이상합니다. 또한 질문은 데이터 스택으로 호출 스택 만 사용하는 것이 었습니다.
vladich

4

나는 매우 아름다운 재귀 (기능조차도) 너비 우선 탐색 관련 알고리즘을 발견했습니다. 내 생각은 아니지만이 주제에서 언급해야한다고 생각합니다.

크리스 오카 사키 (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
}

아름다운 알고리즘 +1. 그러나 여전히 대기열을 사용하는 것으로 나타났습니다. "규칙 3"자체의 왼쪽은 실제로 대기열에서 제외 처리 작업입니다.
Luke Lee

3

바보 같은 방법 :

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;
}

3

짧은 스칼라 솔루션 은 다음과 같습니다 .

  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)

2

파이썬 구현은 다음과 같습니다.

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)

2

다음은 재귀 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]
}

2

다음은 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])

2

다음은 사이클이없는 그래프에서 작동하는 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 = []

2

언어가 생성기와 같은 것을 지원하면 bfs를 동시 재귀 적으로 수행 할 수 있다는 점에서 센트를 최고 답변 에 추가 하고 싶습니다.

우선 @Tanzelax의 답변은 다음과 같습니다.

폭 우선 순회는 전통적으로 스택이 아닌 대기열을 사용합니다. 대기열과 스택의 특성은 거의 반대이므로 보조 저장소 (대기열) 인 호출 스택 (스택, 즉 이름)을 사용하려고하면 거의 실패하게됩니다.

실제로 일반 함수 호출 스택은 일반 스택처럼 작동하지 않습니다. 그러나 생성기 함수는 함수 실행을 일시 중지하므로 노드의 더 깊은 하위 항목을 탐색하지 않고도 다음 수준의 노드 하위 항목을 생성 할 수 있습니다.

다음 코드는 파이썬에서 재귀 bfs입니다.

def bfs(root):
  yield root
  for n in bfs(root):
    for c in n.children:
      yield c

직관은 다음과 같습니다.

  1. bfs first는 루트를 첫 번째 결과로 반환합니다
  2. bfs 시퀀스가 ​​이미 있다고 가정하면 bfs의 다음 레벨 요소는 시퀀스에서 이전 노드의 직계 자식입니다.
  3. 위의 두 절차를 반복하십시오

파이썬을 모르지만 코드 가이 C # 코드로 변환된다고 생각합니다 . BFS 순회를 수행하지만 stackoverflow 예외와 함께 충돌합니다. 나는 지금까지 그 이유를 알지 못했습니다. 그러나 알고리즘이 중지되도록 수정했습니다 (아마도 더 잘 수행됩니다). 내 작업 샘플을 여기에서 찾으 십시오 .
아담 시몬

1

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;
}

2
다른 뷰어의 경우 : 이것은 전체 트리를 배열 로 구현하는 예입니다 . 특히 @Justin은 사전 주문 순회를 수행하는 동안 노드 값 (BFS 순서)을 적절한 BFS 색인의 배열로 저장합니다. 이를 통해 호출 함수는 배열을 통해 선형으로 반복하여 BFS 순서로 값을 인쇄 할 수 있습니다. 이 일반 설명을 참조하십시오. 참고 : 호출 함수는 완전하지 않은 트리의 경우를 처리해야합니다.
bunkerdive

1

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)

나는 이것이 무한 루프에 빠질 수 있다고 생각합니다. 꼭짓점은 방문으로 표시되고 있지만 다시 방문하기 전에 방문을 테스트하지 않습니다.

이 스 니펫은 BFS가 아닌 DFS와 유사합니다.
Dení

1

바이너리 (또는 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));
}

0
#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;
}

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

0

다음은 루프와 큐를 사용하지 않고 양방향 그래프의 너비 우선 탐색을 완전히 재귀 적으로 구현하는 코드입니다.

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


0

이진 트리에 대한 재귀 너비 우선 검색 알고리즘의 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();
    });
}

0

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;
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.