Executando a Primeira Pesquisa de Largura Recursivamente


152

Digamos que você deseje implementar uma pesquisa pela primeira vez de uma árvore binária de forma recursiva . Como você faria isso?

É possível usar apenas a pilha de chamadas como armazenamento auxiliar?


14
pergunta muito boa. isso não é nada simples. basicamente, você está pedindo para implementar um BFS usando apenas uma pilha.
Sisis 31/03/10

4
recursivamente com apenas uma pilha? isso machuca minha cabeça.
precisa

11
Eu costumo usar uma pilha para remover comportamento recursivo
Newtopian

Se eu usar o BFS em um heap máximo, gostaria de saber se as soluções fornecidas abaixo funcionam corretamente? Alguma ideia ?
Jay D

Respostas:


122

(Suponho que este seja apenas algum tipo de exercício de pensamento, ou mesmo uma pergunta de lição de casa / entrevista de truque, mas suponho que eu possa imaginar um cenário bizarro em que não seja permitido nenhum espaço de pilha por algum motivo [algum costume muito ruim gerenciador de memória? alguns problemas bizarros de tempo de execução / sistema operacional?] enquanto você ainda tem acesso à pilha ...)

A travessia da primeira largura tradicionalmente usa uma fila, não uma pilha. A natureza de uma fila e uma pilha são praticamente opostas, portanto, tentar usar a pilha de chamadas (que é uma pilha, daí o nome) como o armazenamento auxiliar (uma fila) está fadada ao fracasso, a menos que você esteja fazendo algo estupidamente ridículo com a pilha de chamadas que você não deveria ser.

Da mesma forma, a natureza de qualquer recursão não-cauda que você tenta implementar é basicamente adicionar uma pilha ao algoritmo. Isso deixa de ser a primeira pesquisa em uma árvore binária e, portanto, o tempo de execução e outros enfeites para o BFS tradicional não se aplicam mais completamente. Obviamente, você sempre pode transformar trivialmente qualquer loop em uma chamada recursiva, mas isso não é nenhum tipo de recursão significativa.

No entanto, existem maneiras, como demonstrado por outros, de implementar algo que segue a semântica do BFS a algum custo. Se o custo da comparação for caro, mas a travessia de nó for barata, como o @Simon Buchan , você pode simplesmente executar uma pesquisa iterativa em profundidade, processando apenas as folhas. Isso significaria nenhuma fila crescente armazenada no heap, apenas uma variável de profundidade local e pilhas sendo construídas repetidamente na pilha de chamadas à medida que a árvore é percorrida repetidamente. E como o @Patrick observou, uma árvore binária apoiada por uma matriz é normalmente armazenada na ordem da primeira travessia de largura de qualquer maneira, portanto, uma pesquisa de largura seria trivial, também sem a necessidade de uma fila auxiliar.


10
Este é realmente apenas um exercício de pensamento. Não consigo imaginar uma situação em que você realmente queira fazer isso. Obrigado pela resposta bem pensada!
Nate

2
" mas suponho que eu possa imaginar algum cenário bizarro em que não seja permitido nenhum espaço de heap por algum motivo ": não sei, posso imaginar um ambiente incorporado em que apenas a pilha (junto com qualquer espaço de memória somente leitura) esteja disponível (é na verdade, é muito fácil e eficiente escrever software sem usar o heap, se você souber exatamente o que seu programa fará, o que geralmente acontece no software incorporado. Portanto, não é tão "bizarro" para mim. Incomum, talvez, mas não bizarro.
Thomas

Acho que sua resposta pode conter uma referência a este artigo ( ibm.com/developerworks/aix/library/au-aix-stack-tree-traversal ). Ele mostra uma implementação sobre o que você escreveu na segunda parte de sua resposta
inclua

25

Se você usar uma matriz para fazer backup da árvore binária, poderá determinar o próximo nó algebricamente. se ié um nó, seus filhos podem ser encontrados em2i + 1 (para o nó esquerdo) e 2i + 2(para o nó direito). O próximo vizinho de um nó é dado por i + 1, a menos que iseja um poder de2

Aqui está o pseudocódigo para uma implementação muito ingênua da primeira pesquisa de largura em uma árvore de pesquisa binária baseada em array. Isso pressupõe uma matriz de tamanho fixo e, portanto, uma árvore de profundidade fixa. Ele examinará os nós sem pai e poderá criar uma pilha incontrolavelmente grande.

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
Agradável. Eu negligenciei o fato de que estamos lidando com uma árvore binária. Os índices podem ser atribuídos usando um DFS. BTW, você esqueceu um retorno falso no primeiro caso.
Sisis 31/03/10

Eu acho que prefiro o método de fila; Retorno adicionado falso.
Patrick McMurchie 31/03/10

1
Inteligente. A idéia de armazenar os nós em uma matriz e referenciá-los algebricamente não me ocorreu.
Nate

19

Não consegui encontrar uma maneira de fazê-lo completamente recursivo (sem nenhuma estrutura de dados auxiliar). Mas se a fila Q for passada por referência, você poderá ter a seguinte função recursiva boba:

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

     BFS(Q)
}

6
Esta é uma maneira não natural, de adicionar funções recursivas para limpar e corrigir.
Mysterion

Discordo completamente - acho mais natural - e também mais útil; você pode estender esse método para passar o estado de funcionamento à medida que passa pelas camadas
Tom Golden

15

O método a seguir usou um algoritmo DFS para obter todos os nós em uma profundidade específica - o mesmo que fazer BFS para esse nível. Se você descobrir a profundidade da árvore e fazer isso para todos os níveis, os resultados serão os mesmos de um 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);
}

Encontrar a profundidade de uma árvore é um pedaço de bolo:

public int MaxDepth(Tree root) {
    if (root == null) {
        return 0;
    } else {
        return Math.Max(MaxDepth(root.Left), MaxDepth(root.Right)) + 1;
    }
}

Preste um pouco mais de atenção à sua formatação de código. Eu fiz algumas mudanças.
Micha

Mas, espere ... isso é um DFS em vez de BFS? Porque PrintLevelNodes não retorna até que levelseja zero.
Herrington Darkholme

1
@HerringtonDarkholme, Correto. Ele pesquisa DFS, mas gera valores como se tivesse feito um BFS para um nível. Obrigado por apontar isso.
Sanj

1
@Sanjay, esta realmente é uma boa demonstração de como alguém pode executar alguma ação em nós na ordem DFS. Não é necessariamente como alguém realmente "tocaria" nós na ordem DFS, mas certamente permitirá "agir" recursivamente em nós na ordem DFS, nesse caso, imprimindo seus valores.
bunkerdive

8

Uma recursão simples de BFS e DFS em Java:
basta pressionar / oferecer o nó raiz da árvore na pilha / fila e chamar essas funções.

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
É um pouco estranho passar a pilha como parâmetro para o DFS, porque você já tem uma pilha implícita. A questão também era usar apenas a pilha de chamadas como uma estrutura de dados.
precisa saber é

4

Eu encontrei um algoritmo relacionado à travessia de largura-primeiro recursivo (até funcional) muito bonito. Não é minha ideia, mas acho que deve ser mencionado neste tópico.

Chris Okasaki explica seu algoritmo de numeração em primeiro lugar no ICFP 2000 em http://okasaki.blogspot.de/2008/07/breadth-first-numbering-algorithm-in.html muito claramente com apenas três figuras.

A implementação Scala do Debasish Ghosh, encontrada em http://debasishg.blogspot.de/2008/09/breadth-first-numbering-okasakis.html , é:

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 para o belo algoritmo. No entanto, eu o encontrei ainda usando uma fila. O lado esquerdo da "Regra 3" é na verdade as operações de desenfileiramento e enfileiramento.
Luke Lee

3

O jeito idiota:

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

Aqui está a solução Scala curta :

  def bfs(nodes: List[Node]): List[Node] = {
    if (nodes.nonEmpty) {
      nodes ++ bfs(nodes.flatMap(_.children))
    } else {
      List.empty
    }
  }

A idéia de usar o valor de retorno como acumulador é bem adequada. Pode ser implementado em outros idiomas da mesma maneira, apenas certifique-se de que sua função recursiva processe a lista de nós .

Listagem de código de teste (usando a árvore de teste @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())))
  }
}

Resultado:

List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)

2

Aqui está uma implementação python:

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

Aqui está uma implementação do Scala 2.11.4 do BFS recursivo. Eu sacrifiquei a otimização da chamada de cauda por brevidade, mas a versão do TCOd é muito semelhante. Veja também a publicação de @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

O seguinte me parece bastante natural, usando Haskell. Itere recursivamente sobre os níveis da árvore (aqui coleciono nomes em uma grande sequência ordenada para mostrar o caminho através da árvore):

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

Aqui está uma implementação transversal Python recursiva do BFS, trabalhando para um gráfico sem ciclo.

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

Eu gostaria de adicionar meus centavos à resposta principal , pois se o idioma suportar algo como gerador, o bfs poderá ser executado de forma co-recursiva.

Para começar, a resposta de @ Tanzelax diz:

A travessia da primeira largura tradicionalmente usa uma fila, não uma pilha. A natureza de uma fila e uma pilha são praticamente opostas, portanto, tentar usar a pilha de chamadas (que é uma pilha, daí o nome) como o armazenamento auxiliar (uma fila) está praticamente fadada ao fracasso

De fato, a pilha de chamadas de funções comuns não se comportará como uma pilha normal. Mas a função de gerador suspenderá a execução da função, dando-nos a chance de gerar o próximo nível de filhos dos nós sem nos aprofundar nos descendentes mais profundos do nó.

O código a seguir é bfs recursivo em Python.

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

A intuição aqui é:

  1. o bfs primeiro retornará a raiz como primeiro resultado
  2. suponha que já tenhamos a sequência bfs, o próximo nível de elementos no bfs são os filhos imediatos do nó anterior na sequência
  3. repita os dois procedimentos acima

Não conheço Python, mas acho que seu código se traduz nesse código c # . Ele faz a travessia do BFS, mas trava com uma exceção do stackoverflow. Ainda não descobri o porquê. No entanto, modifiquei o algoritmo para que ele pare (e provavelmente tenha um desempenho melhor). Você encontra minha amostra de trabalho aqui .
Adam Simon

1

Eu tive que implementar uma passagem heap que sai em uma ordem BFS. Na verdade, não é BFS, mas realiza a mesma tarefa.

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
Para outros visualizadores: este é um exemplo de implementação de uma árvore completa em uma matriz; Especificamente, @Justin está fazendo uma passagem de pré-encomenda, durante a qual ele salva os valores dos nós (na ordem BFS) em uma matriz no índice BFS apropriado. Isso permite que a função de chamada itere linearmente através da matriz, imprimindo valores na ordem BFS. Consulte esta descrição geral Nota: a função de chamada deve lidar com o caso de árvores não completas.
bunkerdive

1

Seja v o vértice inicial

Seja G o gráfico em questão

A seguir está o pseudo-código sem usar a fila

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)

Acho que isso pode ficar preso em um loop infinito - os vértices estão sendo marcados como visitados, mas nunca são testados quanto à visitação antes de voltar a ocorrer.

Esse snippet é semelhante ao DFS, e não ao BFS
Dení

1

O BFS para uma árvore binária (ou n-ária) pode ser feito recursivamente sem filas, como a seguir (aqui em 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;
    }
}

Um exemplo de impressão transversal números 1 a 12 em ordem crescente:

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

Aqui está uma implementação JavaScript que falsifica a amplitude da primeira travessia com a recursão da profundidade primeiro. Estou armazenando os valores do nó em cada profundidade dentro de uma matriz, dentro de um hash. Se um nível já existe (temos uma colisão), apenas pressionamos a matriz nesse nível. Você também pode usar uma matriz em vez de um objeto JavaScript, já que nossos níveis são numéricos e podem servir como índices de matriz. Você pode retornar nós, valores, converter em uma lista vinculada ou o que quiser. Estou apenas retornando valores por uma questão de simplicidade.

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 ] } */

Aqui está um exemplo de amplitude real da primeira largura usando uma abordagem iterativa.

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

A seguir, é apresentado meu código para implementação completamente recursiva da primeira pesquisa de largura de um gráfico bidirecional sem usar loop e fila.

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

Implementação em C # do algoritmo de pesquisa recursiva da primeira largura para uma árvore binária.

Visualização de dados em árvore binária

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

Se você deseja que o algoritmo funcione não apenas com a árvore binária, mas com gráficos, o que pode ter dois ou mais nós que apontam para o mesmo outro nó, você deve evitar o ciclo próprio mantendo a lista dos nós já visitados. A implementação pode ser assim.

Visualização de dados do gráfico

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

Eu criei um programa usando c ++ que também está trabalhando em gráfico conjunto e separado.

    #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;
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.