Esecuzione ampia prima della ricerca ricorsivamente


152

Supponiamo che tu abbia voluto ricorrere in modo ricorsivo all'ampiezza di un albero binario . Come lo faresti?

È possibile utilizzare solo lo stack di chiamate come memoria ausiliaria?


14
ottima domanda. questo non è affatto semplice. in pratica stai chiedendo di implementare un BFS usando solo uno stack.
Sisis,

4
ricorsivamente con solo uno stack? questo mi fa male alla testa.
Kevin Friedheim,

11
Di solito uso uno stack per rimuovere il comportamento ricorsivo
Newtopian

Se utilizzo BFS su un heap Max, mi chiedo se le soluzioni fornite di seguito funzionano correttamente? Qualche idea ?
Jay D,

Risposte:


123

(Suppongo che questo sia solo un tipo di esercizio di pensiero, o anche una domanda a casa per i compiti / intervista, ma suppongo che potrei immaginare uno scenario bizzarro in cui non ti è concesso alcun spazio heap per qualche motivo [qualche usanza davvero brutta gestore della memoria? alcuni bizzarri problemi di runtime / OS?] mentre hai ancora accesso allo stack ...)

Il primo attraversamento usa tradizionalmente una coda, non una pila. La natura di una coda e uno stack sono praticamente opposti, quindi provare a utilizzare lo stack di chiamate (che è uno stack, da cui il nome) come memoria ausiliaria (una coda) è quasi destinato al fallimento, a meno che non lo si stia facendo qualcosa di stupidamente ridicolo con lo stack di chiamate che non dovresti essere.

Allo stesso modo, la natura di qualsiasi ricorsione non-coda che si tenta di implementare è essenzialmente l'aggiunta di uno stack all'algoritmo. Questo non rende più ampia la prima ricerca su un albero binario e quindi il runtime e quant'altro per BFS tradizionale non si applicano più completamente. Ovviamente, puoi sempre trasformare banalmente qualsiasi loop in una chiamata ricorsiva, ma non è una sorta di ricorsione significativa.

Tuttavia, ci sono modi, come dimostrato da altri, di implementare qualcosa che segue la semantica di BFS a un certo costo. Se il costo del confronto è costoso ma l'attraversamento dei nodi è economico, come ha fatto @Simon Buchan , puoi semplicemente eseguire una ricerca iterativa in profondità, elaborando solo le foglie. Ciò significherebbe che nessuna coda crescente viene memorizzata nell'heap, solo una variabile di profondità locale e che gli stack vengono accumulati più e più volte sullo stack di chiamate mentre l'albero viene attraversato più e più volte. E come ha notato @Patrick , un albero binario supportato da un array è in genere memorizzato in ordine di larghezza per primo, quindi una ricerca in profondità sarebbe banale, anche senza la necessità di una coda ausiliaria.


10
Questo è davvero solo un esercizio di pensiero. Non riesco davvero a immaginare una situazione in cui vorresti davvero farlo. Grazie per la risposta ben ponderata!
Nate,

2
" ma suppongo di poter immaginare uno scenario bizzarro in cui per qualche motivo non ti è permesso alcun spazio heap ": non so, posso immaginare un ambiente incorporato in cui è disponibile solo lo stack (insieme a qualsiasi spazio di memoria di sola lettura) (è in realtà è abbastanza semplice ed efficiente scrivere software senza usare l'heap se sai esattamente cosa farà il tuo programma, che di solito è il caso del software incorporato). Quindi non è così "bizzarro" per me. Insolito, forse, ma non bizzarro.
Thomas,

Penso che la tua risposta possa contenere un riferimento a questo articolo ( ibm.com/developerworks/aix/library/au-aix-stack-tree-traversal ). Mostra un'implementazione di ciò che hai scritto nella seconda parte della tua risposta
compreso il

25

Se si utilizza un array per eseguire il backup dell'albero binario, è possibile determinare il nodo successivo algebricamente. se iè un nodo, i suoi figli possono essere trovati in 2i + 1(per il nodo sinistro) e 2i + 2(per il nodo destro). Il prossimo vicino di un nodo è dato da i + 1, a meno che non isia un potere di2

Ecco pseudocodice per un'implementazione molto ingenua della prima ricerca della larghezza su un albero di ricerca binaria supportato da array. Ciò presuppone un array di dimensioni fisse e quindi un albero a profondità fissa. Guarderà nodi senza genitori e potrebbe creare uno stack ingestibilmente 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
Bello. Ho trascurato il fatto che abbiamo a che fare con un albero binario. Gli indici possono essere assegnati utilizzando un DFS. A proposito, hai dimenticato un ritorno falso nel primo caso.
Sisis,

Penso di preferire il metodo di accodamento; P. Aggiunto return false.
Patrick McMurchie,

1
Intelligente. L'idea di archiviare i nodi in un array e fare riferimento ad essi algebricamente non mi era venuta in mente.
Nate,

19

Non sono riuscito a trovare un modo per farlo completamente ricorsivo (senza alcuna struttura di dati ausiliaria). Ma se la coda Q viene passata per riferimento, allora puoi avere la seguente funzione ricorsiva della coda sciocca:

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

     BFS(Q)
}

6
Questo è un modo innaturale, per aggiungere ricorsivo alla funzione pulita e corretta.
Mistero

Completamente in disaccordo - lo trovo più naturale - e anche più utile; puoi estendere questo metodo per tramandare lo stato di lavoro mentre attraversi i livelli
Tom Golden,

15

Il seguente metodo ha usato un algoritmo DFS per ottenere tutti i nodi con una profondità particolare, il che è lo stesso che fare BFS per quel livello. Se scopri la profondità dell'albero e lo fai per tutti i livelli, i risultati saranno gli stessi di 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);
}

Trovare la profondità di un albero è un gioco da ragazzi:

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

Si prega di prestare un po 'più attenzione alla formulazione del codice. Ho fatto alcune modifiche.
Micha,

Ma, aspetta ... è un DFS piuttosto che un BFS? Perché PrintLevelNodes non restituisce fino a quando levelè zero.
Herrington Darkholme,

1
@HerringtonDarkholme, Corretto. Esegue la ricerca DFS ma i valori di output sono come se facesse un BFS per un livello. Grazie per averlo sottolineato.
Sanj,

1
@Sanjay, questa è davvero una buona dimostrazione di come si potrebbe eseguire qualche azione sui nodi in ordine DFS. Non è necessariamente come uno "toccherebbe" realmente i nodi in ordine DFS, ma certamente permetterà di "agire" ricorsivamente sui nodi in ordine DFS, in questo caso stampandone i valori.
bunkerdive,

8

Una semplice ricorsione BFS e DFS in Java:
basta spingere / offrire il nodo radice dell'albero nello stack / coda e chiamare queste funzioni.

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
È un po 'strano passare lo stack come parametro per DFS, perché lì hai già uno stack implicito. Inoltre, la domanda era quella di utilizzare solo lo stack di chiamate come struttura di dati.
vladich,

4

Ho trovato un bellissimo algoritmo ricorsivo (anche funzionale) Breadth-First traversal. Non è una mia idea, ma penso che dovrebbe essere menzionato in questo argomento.

Chris Okasaki spiega il suo primo algoritmo di numerazione dell'ICFP 2000 su http://okasaki.blogspot.de/2008/07/breadth-first-numbering-algorithm-in.html in modo molto chiaro con solo 3 immagini.

L'implementazione Scala di Debasish Ghosh, che ho trovato su 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 per il bellissimo algoritmo. Tuttavia, l'ho trovato ancora usando una coda. Il lato sinistro di "Regola 3" è in realtà l'operazione di dequeue e accodamento.
Luke Lee,

3

Il modo stupido:

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

Ecco la breve soluzione Scala :

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

L'idea di utilizzare il valore di ritorno come accumulatore è adatta. Può essere implementato in altre lingue in modo simile, assicurati solo che il tuo elenco di nodi di processo ricorsivo funzioni .

Elenco dei codici di test (utilizzando l'albero di test @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())))
  }
}

Produzione:

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

2

Ecco un'implementazione di 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

Ecco un'implementazione di Scala 2.11.4 di BFS ricorsivo. Ho sacrificato l'ottimizzazione del tail-call per brevità, ma la versione TCOd è molto simile. Vedi anche il post di @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

Quanto segue mi sembra abbastanza naturale, usando Haskell. Scorrere in modo ricorsivo sui livelli dell'albero (qui raccolgo i nomi in una grande stringa ordinata per mostrare il percorso attraverso l'albero):

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

Ecco un'implementazione Python trasversale ricorsiva di BFS, che lavora per un grafico senza 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

Vorrei aggiungere i miei centesimi alla risposta migliore in quanto se la lingua supporta qualcosa come generatore, bfs può essere fatto in modo ricorsivo.

Per cominciare, la risposta di @ Tanzelax recita:

Il primo attraversamento usa tradizionalmente una coda, non una pila. La natura di una coda e uno stack sono praticamente opposti, quindi cercare di usare lo stack di chiamate (che è uno stack, da cui il nome) come memoria ausiliaria (una coda) è quasi destinato al fallimento

In effetti, lo stack della normale chiamata di funzione non si comporterà come uno stack normale. Ma la funzione generatore sospenderà l'esecuzione della funzione in modo da darci la possibilità di cedere il prossimo livello dei figli dei nodi senza scavare in discendenti più profondi del nodo.

Il seguente codice è ricorsivo bfs in Python.

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

L'intuizione qui è:

  1. bfs first restituirà la radice come primo risultato
  2. supponiamo di avere già la sequenza bfs, il prossimo livello di elementi in bfs sono i figli immediati del nodo precedente nella sequenza
  3. ripetere le due procedure precedenti

Non conosco Python ma penso che il tuo codice si traduca in questo codice C # . Esegue l'attraversamento BFS ma si arresta in modo anomalo con un'eccezione stackoverflow. Finora non ho capito il perché. Tuttavia, ho modificato l'algoritmo in modo che si arresti (e funzioni meglio probabilmente). Trovate il mio campione di lavoro qui .
Adam Simon,

1

Ho dovuto implementare un attraversamento dell'heap che genera in un ordine BFS. In realtà non è BFS ma svolge la stessa attività.

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
Per altri visualizzatori: questo è un esempio di implementazione di un albero completo in un array; In particolare, @Justin sta eseguendo un attraversamento del pre-ordine, durante il quale salva i valori dei nodi (in ordine BFS) in una matrice nell'indice BFS appropriato. Ciò consente alla funzione chiamante di scorrere in modo lineare attraverso l'array, stampando i valori nell'ordine BFS. Vedere questa descrizione generale Nota: la funzione chiamante deve gestire il caso di alberi non completi.
bunkerdive,

1

Sia v il vertice iniziale

Lascia che G sia il grafico in questione

Di seguito è riportato lo pseudo codice senza utilizzare la coda

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)

Penso che questo potrebbe rimanere bloccato in un ciclo infinito: i vertici vengono contrassegnati come visitati, ma non vengono mai testati per essere visitati prima di ricominciare.

Questo frammento è simile a DFS piuttosto che a BFS
Dení

1

BFS per un albero binario (o n-ary) può essere fatto in modo ricorsivo senza code come segue (qui in 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 esempio che attraversa i numeri da 1 a 12 in ordine 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

Ecco un'implementazione JavaScript che simula Breadth First Traversal con Depth First ricorsione. Sto memorizzando i valori dei nodi ad ogni profondità all'interno di un array, all'interno di un hash. Se esiste già un livello (abbiamo una collisione), quindi passiamo all'array a quel livello. È possibile utilizzare un array anziché un oggetto JavaScript, poiché i nostri livelli sono numerici e possono fungere da indici di array. È possibile restituire nodi, valori, convertire in un elenco collegato o qualsiasi cosa tu voglia. Sto solo restituendo valori per semplicità.

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

Ecco un esempio dell'attuale Breadth First Traversal usando un approccio 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

Di seguito è riportato il mio codice per l'implementazione completamente ricorsiva di breadth-first-search di un grafico bidirezionale senza utilizzare loop e code.

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

Implementazione C # dell'algoritmo di ricerca ricorsivo in ampiezza per un albero binario.

Visualizzazione dei dati dell'albero 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 })));
}

Se si desidera che l'algoritmo funzioni non solo con l'albero binario ma con i grafici che possono avere due e più nodi che puntano allo stesso altro nodo, è necessario evitare l'autociclaggio tenendo l'elenco dei nodi già visitati. L'implementazione può essere simile a questa.

Visualizzazione dei dati del grafico

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

Ho creato un programma usando c ++ che funziona anche in un grafico congiunto e disgiunto.

    #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;
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.