Kapsamlı İlk Aramayı yinelemeli olarak gerçekleştirme


156

Diyelim ki, bir ikili ağacın enine aramasını yinelemeli olarak gerçekleştirmek istediğinizi varsayalım . Nasıl devam edersin?

Yedek depolama olarak yalnızca çağrı yığınını kullanmak mümkün mü?


15
çok güzel soru. bu hiç de kolay değil. temelde sadece bir yığın kullanarak bir BFS uygulamak istiyorsunuz.
sisis

4
sadece bir yığınla yinelemeli olarak? bu başımı acıtıyor
Kevin Friedheim

11
Yinelemeli davranışı kaldırmak için genellikle bir yığın kullanırım
Newtopian

Bir Max yığınında BFS kullanırsam, aşağıda verilen çözümlerin düzgün çalışıp çalışmadığını merak ediyorum? Düşüncesi olan var mı ?
Jay D

Yanıtlar:


128

(Bunun sadece bir tür düşünce alıştırması, hatta aldatıcı bir ev ödevi / mülakat sorusu olduğunu varsayıyorum, ancak sanırım herhangi bir nedenle yığın alanına izin verilmeyen tuhaf bir senaryo hayal edebiliyorum [gerçekten kötü bir gelenek bellek yöneticisi? bazı tuhaf çalışma zamanı / işletim sistemi sorunları?] hala yığına erişiminiz varken ...)

Enine geçiş geleneksel olarak bir yığın değil bir kuyruk kullanır. Bir kuyruğun ve bir yığının doğası hemen hemen zıttır, bu nedenle çağrı yığınını (bir yığın, dolayısıyla adıdır) yardımcı depolama (bir kuyruk) olarak kullanmaya çalışmak, yapmadığınız sürece başarısızlığa mahkumdur. olmaman gereken çağrı yığınıyla ilgili aptalca saçma bir şey.

Aynı belirteçte, uygulamaya çalıştığınız herhangi bir kuyruk olmayan özyinelemenin doğası, aslında algoritmaya bir yığın eklemektir. Bu, artık bir ikili ağaçta enine ilk aramayı yapmamasını sağlar ve bu nedenle, geleneksel BFS için çalışma zamanı ve hiçbir şey artık tamamen geçerli değildir. Tabii ki, herhangi bir döngüyü her zaman önemsiz bir şekilde özyinelemeli çağrıya dönüştürebilirsiniz, ancak bu herhangi bir anlamlı özyineleme değildir.

Bununla birlikte, başkalarının da gösterdiği gibi, BFS'nin anlambilimini bir miktar maliyetle izleyen bir şeyi uygulamanın yolları vardır. Karşılaştırma maliyeti pahalıysa ancak düğüm geçişi ucuzsa , @ Simon Buchan'ın yaptığı gibi , yalnızca yaprakları işleyerek derinlemesine yinelemeli bir arama çalıştırabilirsiniz. Bu, yığında saklanan büyüyen bir kuyruk olmadığı anlamına gelir, yalnızca yerel bir derinlik değişkeni ve ağaç defalarca dolaşırken çağrı yığını üzerinde yığınlar tekrar tekrar oluşturulur. Ve @Patrick'in belirttiği gibi, bir dizi tarafından desteklenen bir ikili ağaç, yine de genellikle genişlik-ilk geçiş sırasına göre depolanır, bu nedenle, bunun üzerinde bir yardımcı kuyruğa ihtiyaç duymadan kapsamlı bir ilk arama önemsiz olacaktır.


10
Bu gerçekten sadece bir düşünce egzersizidir. Bunu gerçekten yapmak isteyeceğiniz bir durumu gerçekten hayal edemiyorum. İyi düşünülmüş cevap için teşekkürler!
Nate

2
" ama sanırım herhangi bir nedenle herhangi bir yığın alanına izin verilmeyen bazı tuhaf senaryolar hayal edebiliyorum ": dunno, yalnızca yığının (salt okunur bellek alanıyla birlikte) mevcut olduğu gömülü bir ortam hayal edebiliyorum (bu programınızın tam olarak ne yapacağını biliyorsanız, bu genellikle gömülü yazılımlarda olduğu gibi, yığını kullanmadan yazılım yazmak oldukça kolay ve verimli olur. Yani bu bana "tuhaf" değil. Olağandışı, belki ama tuhaf değil.
Thomas

Cevabınızın bu makaleye bir referans içerebileceğini düşünüyorum ( ibm.com/developerworks/aix/library/au-aix-stack-tree-traversal ). Cevabınızın ikinci bölümünde yazdıklarınızla ilgili bir uygulama gösterir
2016

25

İkili ağacı desteklemek için bir dizi kullanırsanız, bir sonraki düğümü cebirsel olarak belirleyebilirsiniz. ibir düğüm ise , alt öğeleri 2i + 1(sol düğüm için) ve 2i + 2(sağ düğüm için) bulunabilir. Bir düğümün sonraki komşusu, gücü i + 1olmadığı sürece tarafından verilir i.2

Burada, dizi destekli ikili arama ağacında genişlik ilk aramanın çok naif bir uygulaması için sözde kod var. Bu, sabit boyutlu bir dizi ve dolayısıyla sabit bir derinlik ağacı varsayar. Ebeveyn olmayan düğümlere bakacak ve yönetilemeyecek kadar büyük bir yığın oluşturabilir.

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
Güzel. İkili bir ağaçla uğraştığımız gerçeğini gözden kaçırdım. Dizinler bir DFS kullanılarak atanabilir. BTW, ilk durumda yanlış dönüşü unuttunuz.
sisis

Sanırım kuyruk yöntemini tercih ediyorum; P. Yanlış dönüş eklendi.
Patrick McMurchie

1
Zekice. Düğümleri bir dizide saklama ve onlara cebirsel olarak gönderme fikri aklıma gelmemişti.
Nate

19

Tamamen özyinelemeli yapmanın bir yolunu bulamadım (herhangi bir yardımcı veri yapısı olmadan). Ancak, Q kuyruğu referans olarak geçerse, aşağıdaki aptalca kuyruk özyinelemeli işlevine sahip olabilirsiniz:

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

     BFS(Q)
}

6
Bu, temiz ve doğru işleve özyinelemeli eklemenin doğal olmayan bir yoludur.
Gizemli

Tamamen katılmıyorum - bunu daha doğal buluyorum - ve ayrıca daha yararlı; katmanlardan geçerken çalışma durumunu geçmek için bu yöntemi uzatabilirsiniz
Tom Golden

15

Aşağıdaki yöntem, belirli bir derinlikteki tüm düğümleri almak için bir DFS algoritması kullanmıştır - bu, o seviye için BFS yapmakla aynıdır. Ağacın derinliğini bulursanız ve bunu tüm seviyeler için yaparsanız, sonuçlar BFS ile aynı olacaktır.

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

Bir ağacın derinliğini bulmak çocuk oyuncağı:

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

Lütfen kod biçimlendirmenize biraz daha dikkat edin. Bazı değişiklikler yaptım.
Micha

Ama durun ... bu BFS'den çok bir DFS mi? Çünkü PrintLevelNodes levelsıfır olana kadar dönmez .
Herrington Darkholme

1
@HerringtonDarkholme, Doğru. DFS araması yapar, ancak değerleri bir düzey için BFS yapmış gibi verir. Bunu işaret ettiğiniz için teşekkürler.
Sanj

1
@Sanjay, bu gerçekten DFS sırasındaki düğümler üzerinde bazı eylemlerin nasıl gerçekleştirilebileceğinin iyi bir göstergesidir. DFS sırasına göre düğümlere nasıl "dokunulacağı" zorunlu değildir, ancak DFS sırasına göre düğümler üzerinde özyinelemeli olarak "işlem yapılmasına" kesinlikle izin verir, bu durumda değerleri yazdırılır.
bunkerdive

8

Java'da basit bir BFS ve DFS özyinelemesi:
Yığın / kuyruktaki ağacın kök düğümünü itin / sunun ve bu işlevleri çağırın.

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
Yığın DFS için bir parametre olarak geçirilmesi biraz garip çünkü orada zaten örtük yığınız var. Ayrıca soru, veri yapısı olarak yalnızca çağrı yığınını kullanmaktı.
vladich

4

Çok güzel özyinelemeli (hatta işlevsel) Genişlik-İlk geçişle ilgili algoritma buldum. Benim fikrim değil ama bence bu konuya değinilmesi gerekiyor.

Chris Okasaki, ICFP 2000'den gelen ilk numaralandırma algoritmasını http://okasaki.blogspot.de/2008/07/breadth-first-numbering-algorithm-in.html adresinde yalnızca 3 resimle çok net bir şekilde açıklıyor .

Http://debasishg.blogspot.de/2008/09/breadth-first-numbering-okasakis.html adresinde bulduğum Debasish Ghosh'un Scala uygulaması :

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
}

Güzel algoritma için +1. Ancak, hala bir kuyruk kullanırken buldum. "Kural 3" ün sol tarafı aslında kuyruktan çıkarma ve kuyruğa alma işlemidir.
Luke Lee

3

Aptalca yol:

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

İşte kısa Scala çözümü:

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

Geri dönüş değerini biriktirici olarak kullanma fikri çok uygundur. Diğer dillerde de benzer şekilde uygulanabilir, sadece özyinelemeli işlev sürecinizin düğüm listesinin olduğundan emin olun .

Test kodu listesi (@marco test ağacını kullanarak):

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

Çıktı:

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

2

İşte bir python uygulaması:

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

İşte yinelemeli BFS'nin bir Scala 2.11.4 uygulaması. Kısalık için kuyruk arama optimizasyonunu feda ettim, ancak TCOd sürümü çok benzer. Ayrıca @snv'nin gönderisine bakın .

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

Aşağıdakiler Haskell kullanarak bana oldukça doğal görünüyor. Ağacın seviyeleri üzerinde yinelemeli olarak yineleyin (burada ağaçtaki yolu göstermek için adları büyük sıralı bir dizede topluyorum):

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

İşte döngü içermeyen bir grafik için çalışan bir BFS yinelemeli geçiş Python uygulaması.

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

Kuruşlarımı eklemek istiyorum üst yanıta , eğer dil jeneratör gibi bir şeyi destekliyorsa, bfs eş-özyinelemeli olarak yapılabilir.

Başlangıç ​​olarak @ Tanzelax'ın cevabı şu şekildedir:

Enine geçiş geleneksel olarak bir yığın değil bir kuyruk kullanır. Bir kuyruğun ve bir yığının doğası hemen hemen zıttır, bu nedenle çağrı yığınını (bir yığın, dolayısıyla adıdır) yardımcı depolama (bir kuyruk) olarak kullanmaya çalışmak hemen hemen başarısızlığa mahkumdur.

Aslında, sıradan işlev çağrısının yığını, normal bir yığın gibi davranmaz. Ancak jeneratör işlevi, işlevin yürütülmesini askıya alır, bu nedenle bize düğümün daha derin torunlarına girmeden bir sonraki düğüm çocuklarını verme şansı verir.

Aşağıdaki kod Python'da özyinelemeli bfs'dir.

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

Buradaki sezgi şudur:

  1. bfs ilk sonuç olarak kökü döndürecektir
  2. Zaten bfs dizisine sahip olduğumuzu varsayalım, bfs'deki bir sonraki öğe düzeyi, dizideki önceki düğümün hemen çocuklarıdır
  3. yukarıdaki iki prosedürü tekrarlayın

Python bilmiyorum ama kodunuzun bu C # koduna çevrildiğini düşünüyorum . BFS çapraz geçişi yapar ancak bir yığın aşımı istisnası ile çöker. Şimdiye kadar nedenini çözemedim. Ancak, algoritmayı duracak (ve muhtemelen daha iyi performans gösterecek) şekilde değiştirdim. Çalışma örneğimi burada bulabilirsiniz .
Adam Simon

1

BFS siparişinde çıktı veren bir yığın geçişi uygulamak zorunda kaldım. Aslında BFS değildir, ancak aynı görevi yerine getirir.

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
Diğer izleyiciler için: bu, bir dizide eksiksiz bir ağaç uygulamaya bir örnektir ; Özellikle @Justin, düğüm değerlerini (BFS sırasına göre) uygun BFS dizinindeki bir diziye kaydettiği bir ön sipariş geçişi yapıyor. Bu, çağrı işlevinin dizi boyunca doğrusal olarak yinelemesine ve değerleri BFS sırasına yazdırmasına olanak tanır. Bu genel açıklamaya bakın Not: çağıran işlev, tam olmayan ağaçların durumunu ele almalıdır.
bunkerdive

1

V başlangıç ​​noktası olsun

G söz konusu grafik olsun

Sırayı kullanmadan sözde kod aşağıdadır

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)

Bunun sonsuz bir döngüde sıkışmış olabileceğini düşünüyorum - köşeler ziyaret edilmiş olarak işaretleniyor, ancak tekrar tekrarlamadan önce ziyaret edilmişlik için asla test edilmiyorlar.

Bu pasaj, BFS yerine
DFS'ye

1

Bir ikili (veya n-ary) ağaç için BFS, aşağıdaki gibi sıralar olmadan özyinelemeli olarak yapılabilir (burada Java'da):

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

Artan sırada 1-12 numaralı bir çapraz baskı numarası örneği:

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

Burada, Önce Derinlik özyinelemeli İlk Genişlik Geçişini taklit eden bir JavaScript Uygulaması var. Düğüm değerlerini bir karmanın içinde bir dizi içinde her derinlikte depoluyorum. Bir seviye zaten mevcutsa (bir çarpışmamız varsa), bu yüzden sadece o seviyedeki diziyi itiyoruz. Seviyelerimiz sayısal olduğundan ve dizi indeksleri olarak hizmet edebildiğinden, JavaScript nesnesi yerine bir dizi de kullanabilirsiniz. Düğümleri, değerleri döndürebilir, Bağlantılı Listeye dönüştürebilir veya istediğiniz her şeyi yapabilirsiniz. Basitlik uğruna değerleri döndürüyorum.

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

Yinelemeli bir yaklaşım kullanan gerçek İlk Genişlik Geçişinin bir örneğini burada bulabilirsiniz.

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şağıda, döngü ve kuyruk kullanmadan çift yönlü bir grafiğin enine ilk aramasının tamamen yinelemeli uygulaması için kodum yer almaktadır.

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

Bir ikili ağaç için özyinelemeli genişlik ilk arama algoritmasının C # uygulaması.

İkili ağaç veri görselleştirme

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

Algoritmanın yalnızca ikili ağaçla değil, aynı başka düğüme işaret eden iki veya daha fazla düğüme sahip olabilecek grafiklerle çalışmasını istiyorsanız, önceden ziyaret edilen düğümlerin listesini tutarak kendi kendine döngüyü önlemek zorundasınız. Uygulama şuna benziyor olabilir.

Grafik veri görselleştirme

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 ++ kullanarak ortak ve ayrık grafikte de çalışan bir program yaptım.

    #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;
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.