Realizar Breadth First Search de forma recursiva


152

Supongamos que desea implementar una búsqueda amplia de un árbol binario de forma recursiva . ¿Cómo lo harías?

¿Es posible usar solo la pila de llamadas como almacenamiento auxiliar?


14
Muy buena pregunta. Esto no es sencillo en absoluto. básicamente, está pidiendo implementar un BFS utilizando solo una pila.
sisis

44
recursivamente con solo una pila? Esto me duele la cabeza.
Kevin Friedheim

11
Usualmente uso una pila para eliminar el comportamiento recursivo
Newtopian

Si uso BFS en un montón Max, me pregunto si las soluciones que figuran a continuación funcionan correctamente. Alguna idea ?
Jay D

Respuestas:


123

(Supongo que esto es solo un tipo de ejercicio de pensamiento, o incluso una pregunta de tarea / entrevista engañosa, pero supongo que podría imaginar un escenario extraño en el que no se le permite ningún espacio de almacenamiento dinámico por alguna razón [alguna muy mala costumbre administrador de memoria? algunos problemas extraños de tiempo de ejecución / sistema operativo?] mientras todavía tiene acceso a la pila ...)

El primer recorrido de ancho usa tradicionalmente una cola, no una pila. La naturaleza de una cola y una pila son bastante opuestas, por lo que tratar de usar la pila de llamadas (que es una pila, de ahí el nombre) como almacenamiento auxiliar (una cola) está prácticamente condenada al fracaso, a menos que esté haciendo algo estúpidamente ridículo con la pila de llamadas que no deberías ser.

En el mismo token, la naturaleza de cualquier recursión sin cola que intente implementar es esencialmente agregar una pila al algoritmo. Esto hace que ya no se amplíe la primera búsqueda en un árbol binario y, por lo tanto, el tiempo de ejecución y otras cosas para BFS tradicional ya no se aplican por completo. Por supuesto, siempre puede convertir trivialmente cualquier bucle en una llamada recursiva, pero eso no es ningún tipo de recursión significativa.

Sin embargo, hay formas, como lo demuestran otros, para implementar algo que sigue la semántica de BFS a un costo. Si el costo de la comparación es costoso pero el recorrido del nodo es barato, entonces, como lo hizo @Simon Buchan , simplemente puede ejecutar una búsqueda iterativa en profundidad, solo procesando las hojas. Esto significaría que no hay una cola creciente almacenada en el montón, solo una variable de profundidad local, y las pilas que se acumulan una y otra vez en la pila de llamadas a medida que se recorre el árbol una y otra vez. Y como señaló @Patrick , un árbol binario respaldado por una matriz generalmente se almacena en orden transversal primero, por lo que una búsqueda de primer orden sería trivial, también sin necesidad de una cola auxiliar.


10
De hecho, esto es solo un ejercicio de pensamiento. Realmente no puedo imaginar una situación en la que realmente quieras hacer esto. Gracias por la respuesta bien pensada!
Nate

2
" pero supongo que podría imaginarme un escenario extraño en el que no se le permite ningún espacio de almacenamiento dinámico por alguna razón ": no sé, puedo imaginar un entorno incrustado donde solo la pila (junto con cualquier espacio de memoria de solo lectura) esté disponible (es en realidad, es bastante fácil y eficiente escribir software sin usar el montón si sabe exactamente qué va a hacer su programa, que suele ser el caso en el software integrado). Entonces no es tan "extraño" para mí. Inusual, tal vez, pero no extraño.
Thomas

Creo que su respuesta puede contener una referencia a este artículo ( ibm.com/developerworks/aix/library/au-aix-stack-tree-traversal ). Muestra una implementación sobre lo que escribió en la segunda parte de su respuesta
incudir el

25

Si usa una matriz para respaldar el árbol binario, puede determinar el siguiente nodo algebraicamente. si ies un nodo, sus hijos se pueden encontrar en 2i + 1(para el nodo izquierdo) y 2i + 2(para el nodo derecho). El siguiente vecino de un nodo está dado por i + 1, a menos que isea ​​un poder de2

Aquí hay un pseudocódigo para una implementación muy ingenua de la primera búsqueda de amplitud en un árbol de búsqueda binaria respaldado por matriz. Esto supone una matriz de tamaño fijo y, por lo tanto, un árbol de profundidad fija. Examinará los nodos sin padres y podría crear una pila inmanejablemente 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
Agradable. Pasé por alto el hecho de que estamos tratando con un árbol binario. Los índices se pueden asignar utilizando un DFS. Por cierto, olvidó una devolución falsa en el primer caso.
sisis

Creo que prefiero el método de colas; P. Agregado retorno falso.
Patrick McMurchie

1
Inteligente. No se me había ocurrido la idea de almacenar los nodos en una matriz y hacer referencia algebraica a ellos.
Nate

19

No pude encontrar una manera de hacerlo completamente recursivo (sin ninguna estructura de datos auxiliar). Pero si la cola Q se pasa por referencia, entonces puede tener la siguiente función recursiva de cola tonta:

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

     BFS(Q)
}

66
Esta es una forma antinatural, para agregar recursivo a la función limpia y correcta.
Mysterion

Completamente en desacuerdo, lo encuentro más natural y también más útil; puede extender este método para pasar el estado de trabajo a medida que avanza por las capas
Tom Golden

15

El siguiente método usó un algoritmo DFS para obtener todos los nodos en una profundidad particular, que es lo mismo que hacer BFS para ese nivel. Si descubre la profundidad del árbol y hace esto para todos los niveles, los resultados serán los mismos que los de un 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 la profundidad de un árbol es pan comido:

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

Presta un poco más de atención al formato de tu código. Hice algunos cambios.
Micha

Pero, espera ... ¿es esto un DFS en lugar de BFS? Porque PrintLevelNodes no regresa hasta que levelsea ​​cero.
Herrington Darkholme

1
@HerringtonDarkholme, correcto. Realiza la búsqueda DFS pero los valores de salida como si hiciera un BFS para un nivel. Gracias por señalar eso.
Sanj

1
@Sanjay, esta es una buena demostración de cómo se puede realizar alguna acción en los nodos en el orden DFS. No es necesariamente cómo uno "tocaría" realmente los nodos en el orden DFS, sino que ciertamente permitirá "actuar" recursivamente en los nodos en el orden DFS, en este caso imprimiendo sus valores.
bunkerdive

8

Una simple recursión BFS y DFS en Java:
simplemente presione / ofrezca el nodo raíz del árbol en la pila / cola y llame a estas funciones.

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

44
Es un poco extraño pasar la pila como parámetro para DFS, porque ya tienes una pila implícita allí. Además, la pregunta era usar solo la pila de llamadas como estructura de datos.
vladich

4

Encontré un algoritmo muy hermoso recursivo (incluso funcional) de Breadth-First transversal. No es mi idea, pero creo que debería mencionarse en este tema.

Chris Okasaki explica su algoritmo de numeración más amplio de ICFP 2000 en http://okasaki.blogspot.de/2008/07/breadth-first-numbering-algorithm-in.html muy claramente con solo 3 imágenes.

La implementación Scala de Debasish Ghosh, que encontré en http://debasishg.blogspot.de/2008/09/breadth-first-numbering-okasakis.html , es:

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 el hermoso algoritmo. Sin embargo, todavía lo encontré usando una cola. El lado izquierdo de la "Regla 3" en sí es en realidad las operaciones de quitar y poner en cola.
Luke Lee

3

La manera tonta:

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

Aquí hay una breve solución de Scala :

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

La idea de utilizar el valor de retorno como acumulador es muy adecuada. Se puede implementar en otros idiomas de manera similar, solo asegúrese de que su función recursiva procese la lista de nodos .

Lista de códigos de prueba (usando el árbol de prueba @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())))
  }
}

Salida:

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

2

Aquí hay una implementación de 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

Aquí hay una implementación de Scala 2.11.4 de BFS recursivo. He sacrificado la optimización de llamadas de cola por brevedad, pero la versión TCOd es muy similar. Ver también la publicación 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

Lo siguiente me parece bastante natural, usando Haskell. Iterar recursivamente sobre los niveles del árbol (aquí recopilo nombres en una gran cadena ordenada para mostrar la ruta a través del árbol):

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

Aquí hay una implementación de Python transversal recursiva de BFS, trabajando para un gráfico sin 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

Me gustaría agregar mis centavos a la respuesta principal en que si el lenguaje admite algo como generador, bfs se puede hacer de forma recursiva.

Para empezar, la respuesta de @ Tanzelax dice:

El primer recorrido de ancho usa tradicionalmente una cola, no una pila. La naturaleza de una cola y una pila son bastante opuestas, por lo que tratar de usar la pila de llamadas (que es una pila, de ahí el nombre) como almacenamiento auxiliar (una cola) está prácticamente condenada al fracaso

De hecho, la pila de llamadas de función ordinarias no se comportará como una pila normal. Pero la función de generador suspenderá la ejecución de la función, por lo que nos da la oportunidad de producir el siguiente nivel de hijos de nodos sin profundizar en los descendientes más profundos del nodo.

El siguiente código es bfs recursivo en Python.

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

La intuición aquí es:

  1. bfs primero devolverá la raíz como primer resultado
  2. supongamos que ya tenemos la secuencia bfs, el siguiente nivel de elementos en bfs son los hijos inmediatos del nodo anterior en la secuencia
  3. repita los dos procedimientos anteriores

No conozco Python, pero creo que su código se traduce en este código C # . Realiza el recorrido BFS pero se bloquea con una excepción de stackoverflow. No he descubierto el por qué hasta ahora. Sin embargo, modifiqué el algoritmo para que se detenga (y probablemente funcione mejor). Encuentra mi muestra de trabajo aquí .
Adam Simon

1

Tuve que implementar un recorrido del montón que sale en un orden BFS. En realidad no es BFS, pero realiza la misma tarea.

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 otros espectadores: este es un ejemplo de implementación de un árbol completo en una matriz; Específicamente, @Justin está haciendo un recorrido de preorden, durante el cual guarda los valores de los nodos (en orden BFS) en una matriz en el índice BFS apropiado. Esto permite que la función de llamada itere linealmente a través de la matriz, imprimiendo valores en el orden BFS. Consulte esta descripción general Nota: la función de llamada debe manejar el caso de árboles no completos.
bunkerdive

1

Sea v el vértice inicial

Deje G ser el gráfico en cuestión

El siguiente es el pseudocódigo sin usar cola

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)

Creo que esto podría atascarse en un bucle infinito: los vértices se marcan como visitados, pero nunca se prueba su visita antes de volver a repetirse.

Este fragmento es similar a DFS en lugar de BFS
Dení

1

BFS para un árbol binario (o n-ario) se puede hacer de forma recursiva sin colas de la siguiente manera (aquí en 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;
    }
}

Un ejemplo de impresión transversal números 1-12 en orden ascendente:

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

Aquí hay una implementación de JavaScript que simula Breadth First Traversal con Depth First recursión. Estoy almacenando los valores de nodo en cada profundidad dentro de una matriz, dentro de un hash. Si ya existe un nivel (tenemos una colisión), entonces simplemente avanzamos a la matriz en ese nivel. También podría usar una matriz en lugar de un objeto JavaScript, ya que nuestros niveles son numéricos y pueden servir como índices de matriz. Puede devolver nodos, valores, convertirlos en una lista vinculada o lo que desee. Solo estoy devolviendo valores en aras de la simplicidad.

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

Aquí hay un ejemplo del recorrido transversal de Breadth First utilizando un enfoque iterativo.

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

El siguiente es mi código para la implementación completamente recursiva de la búsqueda de amplitud en primer plano de un gráfico bidireccional sin usar bucle y cola.

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

Implementación de C # del algoritmo de búsqueda de amplitud recursiva para un árbol binario.

Visualización de datos de árbol binario.

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

Si desea que el algoritmo funcione no solo con un árbol binario, sino también con gráficos, lo que puede tener dos y más nodos que apuntan al mismo nodo, debe evitar el ciclo automático manteniendo una lista de nodos ya visitados. La implementación puede ser así.

Visualización de datos gráficos

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

Hice un programa usando c ++ que también funciona en gráficos conjuntos y disjuntos.

    #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;
}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.