แสดงการค้นหาแบบกว้างครั้งแรกแบบวนซ้ำ


152

สมมติว่าคุณต้องการที่จะดำเนินการค้นหาในแนวกว้างของต้นไม้ไบนารีซ้ำ คุณจะไปเกี่ยวกับมันได้อย่างไร

เป็นไปได้หรือไม่ที่ใช้ call-stack เท่านั้นเป็นที่เก็บข้อมูลสำรอง?


14
คำถามที่ดีมาก สิ่งนี้ไม่ตรงไปตรงมาเลย โดยทั่วไปคุณกำลังขอให้ใช้ BFS โดยใช้เพียงกองซ้อน
sisis

4
ซ้ำด้วยสแต็กหรือไม่ นี่ทำให้ฉันเจ็บ
Kevin Friedheim

11
ฉันมักจะใช้สแต็กเพื่อลบพฤติกรรมแบบเรียกซ้ำ
Newtopian

ถ้าฉันใช้ BFS ใน Max Max ฉันสงสัยว่าวิธีการแก้ปัญหาด้านล่างนี้ทำงานได้อย่างถูกต้องหรือไม่? ความคิดใด ๆ
Jay D

คำตอบ:


123

(ฉันสมมติว่านี่เป็นแบบฝึกหัดความคิดบางอย่างหรือแม้แต่คำถามหลอกลวง / คำถามสัมภาษณ์ แต่ฉันคิดว่าฉันสามารถจินตนาการถึงสถานการณ์ที่แปลกประหลาดบางอย่างที่คุณไม่ได้รับอนุญาตให้มีพื้นที่กองทับด้วยเหตุผลบางอย่าง ตัวจัดการหน่วยความจำหรือไม่พบปัญหา runtime / OS แปลกประหลาด] ในขณะที่คุณยังสามารถเข้าถึงสแต็ก ... )

การสำรวจเส้นทางแบบกว้างแรกนั้นใช้คิวไม่ใช่สแต็ก ลักษณะของคิวและสแต็กค่อนข้างตรงกันข้ามดังนั้นพยายามใช้ call stack (ซึ่งเป็น stack ดังนั้นชื่อ) เนื่องจากหน่วยเก็บข้อมูลสำรอง (คิว) นั้นค่อนข้างจะล้มเหลวเว้นแต่คุณกำลังทำอยู่ สิ่งที่ไร้สาระอย่างน่าประหลาดใจกับ call stack ที่คุณไม่ควรเป็น

ในโทเค็นเดียวกันลักษณะของการเรียกซ้ำแบบไม่หางใด ๆ ที่คุณพยายามนำมาใช้นั้นเป็นการเพิ่มสแต็กเข้ากับอัลกอริทึม สิ่งนี้ทำให้การค้นหาแบบกว้างครั้งแรกไม่ได้อยู่บนต้นไม้ไบนารีอีกต่อไปดังนั้นเวลาทำงานและสิ่งที่ไม่ได้รับสำหรับ BFS แบบดั้งเดิมจะไม่มีผลอีกต่อไป แน่นอนว่าคุณสามารถเปลี่ยนลูปใด ๆ เป็นการโทรซ้ำแบบเรียกซ้ำได้ แต่นั่นไม่ใช่การเรียกซ้ำที่มีความหมายใด ๆ

อย่างไรก็ตามมีวิธีที่แสดงให้เห็นโดยผู้อื่นในการดำเนินการบางสิ่งบางอย่างที่เป็นไปตามความหมายของ BFS ที่มีค่าใช้จ่ายบางอย่าง หากค่าใช้จ่ายในการเปรียบเทียบมีราคาแพง แต่การสำรวจเส้นทางโหนดนั้นมีราคาถูกเช่นเดียวกับ@Simon Buchanคุณสามารถเรียกใช้การค้นหาเชิงลึกครั้งแรกซ้ำได้เพียงประมวลผลใบไม้ นี่หมายความว่าไม่มีคิวการเติบโตที่เก็บไว้ในกองเพียงแค่ตัวแปรความลึกโลคัลและสแต็กที่ถูกสร้างขึ้นเป็นวรรคเป็นเวรบน call stack ขณะที่ทรีถูกข้ามไปเรื่อย ๆ และตามที่@Patrickระบุไว้ต้นไม้ไบนารีที่ได้รับการสนับสนุนโดยอาเรย์จะถูกเก็บไว้ในลำดับการส่งผ่านข้อมูลแบบกว้างแรกดังนั้นการค้นหาแบบกว้างแรกในเรื่องนั้นจะเป็นเรื่องเล็กน้อยโดยไม่จำเป็นต้องมีคิวช่วย


10
นี่เป็นเพียงการฝึกความคิด ฉันไม่สามารถจินตนาการถึงสถานการณ์ที่คุณต้องการทำสิ่งนี้จริง ๆ ขอบคุณสำหรับคำตอบที่คิดออกดี!
Nate

2
" แต่ฉันคิดว่าฉันสามารถจินตนาการถึงสถานการณ์ที่แปลกประหลาดบางอย่างที่คุณไม่ได้รับอนุญาตให้ใช้พื้นที่ฮีปด้วยเหตุผลบางอย่าง ": ไม่เป็นไรฉันสามารถจินตนาการถึงสภาพแวดล้อมแบบฝังที่มีสแต็ก (พร้อมกับพื้นที่หน่วยความจำแบบอ่านอย่างเดียว) เท่านั้น จริง ๆ แล้วค่อนข้างง่ายและมีประสิทธิภาพในการเขียนซอฟต์แวร์โดยไม่ต้องใช้ heap เลยถ้าคุณรู้แน่ชัดว่าโปรแกรมของคุณกำลังทำอะไรอยู่ซึ่งโดยปกติจะเป็นกรณีของซอฟต์แวร์ฝังตัว) ดังนั้นไม่ใช่ "แปลกประหลาด" สำหรับฉัน ผิดปกติอาจจะ แต่ไม่แปลกประหลาด
โทมัส

ฉันคิดว่าคำตอบของคุณอาจมีการอ้างอิงถึงบทความนี้ ( ibm.com/developerworks/aix/library/au-aix-stack-tree-traversal ) มันแสดงให้เห็นถึงการนำไปปฏิบัติเกี่ยวกับสิ่งที่คุณเขียนในส่วนที่สองของคำตอบของคุณ
รวม

25

หากคุณใช้อาร์เรย์เพื่อสำรองต้นไม้ไบนารีคุณสามารถกำหนดโหนดต่อไปทางพีชคณิต ถ้าiเป็นโหนดก็สามารถพบลูก ๆ ของมันที่2i + 1(สำหรับโหนดด้านซ้าย) และ2i + 2(สำหรับโหนดขวา) โหนดถัดไปของโหนดจะถูกกำหนดโดยi + 1ยกเว้นว่าiเป็นกำลัง2

นี่คือ pseudocode สำหรับการนำไปใช้อย่างไร้เดียงสาของการค้นหาความกว้างครั้งแรกในแผนภูมิการค้นหาแบบไบนารีที่สำรองข้อมูลไว้ นี่ถือว่าเป็นอาร์เรย์ขนาดคงที่และดังนั้นจึงเป็นต้นไม้ความลึกคงที่ มันจะดูที่โหนด parentless และสามารถสร้างสแต็กขนาดใหญ่ที่ไม่สามารถจัดการได้

bintree-bfs(bintree, elt, i)
    if (i == LENGTH)
        return false

    else if (bintree[i] == elt)
        return true

    else 
        return bintree-bfs(bintree, elt, i+1)        

1
ดี ฉันมองข้ามความจริงที่ว่าเรากำลังจัดการกับต้นไม้ไบนารี สามารถกำหนดดัชนีโดยใช้ DFS BTW คุณลืมส่งคืนเท็จในกรณีแรก
sisis

ฉันคิดว่าฉันชอบวิธีการจัดคิวมาก เพิ่มผลตอบแทนที่ผิดพลาด
Patrick McMurchie

1
ฉลาด. แนวคิดของการเก็บโหนดในอาร์เรย์และการอ้างอิงพวกเขาเกี่ยวกับพีชคณิตไม่ได้เกิดขึ้นกับฉัน
เนท

19

ฉันไม่สามารถหาวิธีที่จะเรียกซ้ำได้อย่างสมบูรณ์ (ไม่มีโครงสร้างข้อมูลเสริม) แต่ถ้าคิวคิวถูกส่งผ่านโดยการอ้างอิงคุณสามารถมีฟังก์ชัน recursive แบบ silly tail ต่อไปนี้

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

     BFS(Q)
}

6
นี่คือวิธีที่ผิดธรรมชาติเพื่อเพิ่มฟังก์ชั่นซ้ำเพื่อทำความสะอาดและแก้ไข
Mysterion

ไม่เห็นด้วยอย่างสมบูรณ์ - ฉันคิดว่ามันเป็นธรรมชาติมากกว่า - และมีประโยชน์มากกว่า; คุณสามารถขยายวิธีนี้เพื่อส่งผ่านสถานะการทำงานเมื่อคุณผ่านเลเยอร์
Tom Golden

15

วิธีการต่อไปนี้ใช้อัลกอริทึม DFS เพื่อให้ได้โหนดทั้งหมดในระดับความลึก - ซึ่งเหมือนกับการทำ BFS สำหรับระดับนั้น หากคุณค้นหาความลึกของต้นไม้และทำสิ่งนี้ในทุกระดับผลลัพธ์จะเหมือนกับ BFS

public void PrintLevelNodes(Tree root, int level) {
    if (root != null) {
        if (level == 0) {
            Console.Write(root.Data);
            return;
        }
        PrintLevelNodes(root.Left, level - 1);
        PrintLevelNodes(root.Right, level - 1);
    }
}

for (int i = 0; i < depth; i++) {
    PrintLevelNodes(root, i);
}

การค้นหาความลึกของต้นไม้เป็นเค้ก:

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

โปรดให้ความสำคัญกับการจัดรูปแบบโค้ดของคุณเล็กน้อย ฉันทำการเปลี่ยนแปลงบางอย่าง
ปาสเตอร์

แต่เดี๋ยวก่อน ... นี่คือ DFS มากกว่า BFS หรือไม่ เนื่องจาก PrintLevelNodes ไม่ส่งคืนจนกว่าlevelจะมีค่าเป็นศูนย์
Herrington Darkholme

1
@HerringtonDarkholme ถูกต้อง มันทำการค้นหา DFS แต่เอาท์พุทค่าราวกับว่ามันทำ BFS สำหรับระดับ ขอบคุณสำหรับการชี้ให้เห็นว่า
Sanj

1
@Sanjay นี่เป็นการสาธิตที่ดีว่าผู้ใดจะดำเนินการบางอย่างกับโหนดตามลำดับ DFS ไม่จำเป็นว่าจะต้อง "สัมผัส" โหนดใน DFS-order จริง ๆ แต่จะอนุญาตให้มีการ "กระทำ" ซ้ำบนโหนดตามลำดับ DFS ในกรณีนี้การพิมพ์ค่าของพวกเขา
bunkerdive

8

การเรียกซ้ำ BFS และ DFS อย่างง่ายใน Java:
เพียงแค่กด / เสนอโหนดรากของทรีในสแต็ก / คิวและเรียกใช้ฟังก์ชันเหล่านี้

public static void breadthFirstSearch(Queue queue) {

    if (queue.isEmpty())
        return;

    Node node = (Node) queue.poll();

    System.out.println(node + " ");

    if (node.right != null)
        queue.offer(node.right);

    if (node.left != null)
        queue.offer(node.left);

    breadthFirstSearch(queue);
}

public static void depthFirstSearch(Stack stack) {

    if (stack.isEmpty())
        return;

    Node node = (Node) stack.pop();

    System.out.println(node + " ");

    if (node.right != null)
        stack.push(node.right);

    if (node.left != null)
        stack.push(node.left);

    depthFirstSearch(stack);
}

4
มันค่อนข้างแปลกที่จะผ่านสแต็กเป็นพารามิเตอร์สำหรับ DFS เพราะคุณมีสแต็กโดยปริยายอยู่แล้ว คำถามก็คือใช้ call stack เป็นโครงสร้างข้อมูลเท่านั้น
vladich

4

ฉันพบอัลกอริธึมที่เกี่ยวข้องกับการสำรวจเส้นทางแบบ breadth-traursal ที่สวยงามมาก ไม่ใช่ความคิดของฉัน แต่ฉันคิดว่าควรพูดถึงในหัวข้อนี้

Chris Okasaki อธิบายอัลกอริทึมการกำหนดหมายเลขที่กว้างเป็นอันดับแรกของเขาจาก ICFP 2000 ที่http://okasaki.blogspot.de/2008/07/breadth-first-numbering-algorithm-in.htmlอย่างชัดเจนเพียง 3 รูปเท่านั้น

การติดตั้ง Scala ของ Debasish Ghosh ซึ่งฉันพบได้ที่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 สำหรับอัลกอริทึมที่สวยงาม อย่างไรก็ตามฉันพบว่ามันยังคงใช้งานคิวอยู่ ทางด้านซ้ายของ "กฎ 3" นั้นแท้จริงแล้วเป็นการดำเนินการแบบคิวและการเข้าคิว
ลุคลี

3

วิธีโง่:

template<typename T>
struct Node { Node* left; Node* right; T value; };

template<typename T, typename P>
bool searchNodeDepth(Node<T>* node, Node<T>** result, int depth, P pred) {
    if (!node) return false;
    if (!depth) {
        if (pred(node->value)) {
            *result = node;
        }
        return true;
    }
    --depth;
    searchNodeDepth(node->left, result, depth, pred);
    if (!*result)
        searchNodeDepth(node->right, result, depth, pred);
    return true;
}

template<typename T, typename P>
Node<T>* searchNode(Node<T>* node, P pred) {
    Node<T>* result = NULL;
    int depth = 0;
    while (searchNodeDepth(node, &result, depth, pred) && !result)
        ++depth;
    return result;
}

int main()
{
    // a c   f
    //  b   e
    //    d
    Node<char*>
        a = { NULL, NULL, "A" },
        c = { NULL, NULL, "C" },
        b = { &a, &c, "B" },
        f = { NULL, NULL, "F" },
        e = { NULL, &f, "E" },
        d = { &b, &e, "D" };

    Node<char*>* found = searchNode(&d, [](char* value) -> bool {
        printf("%s\n", value);
        return !strcmp((char*)value, "F");
    });

    printf("found: %s\n", found->value);

    return 0;
}

3

นี่คือวิธีแก้ปัญหาสกาล่าสั้น ๆ:

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

แนวคิดของการใช้ค่าตอบแทนเนื่องจากตัวสะสมมีความเหมาะสม สามารถดำเนินการในภาษาอื่น ๆ ในลักษณะที่คล้ายกันเพียงให้แน่ใจว่ากระบวนการของคุณ recursive ฟังก์ชั่นรายการของโหนด

รายการรหัสทดสอบ (โดยใช้แผนภูมิทดสอบ @marco):

import org.scalatest.FlatSpec

import scala.collection.mutable

class Node(val value: Int) {

  private val _children: mutable.ArrayBuffer[Node] = mutable.ArrayBuffer.empty

  def add(child: Node): Unit = _children += child

  def children = _children.toList

  override def toString: String = s"$value"
}

class BfsTestScala extends FlatSpec {

  //            1
  //          / | \
  //        2   3   4
  //      / |       | \
  //    5   6       7  8
  //  / |           | \
  // 9  10         11  12
  def tree(): Node = {
    val root = new Node(1)
    root.add(new Node(2))
    root.add(new Node(3))
    root.add(new Node(4))
    root.children(0).add(new Node(5))
    root.children(0).add(new Node(6))
    root.children(2).add(new Node(7))
    root.children(2).add(new Node(8))
    root.children(0).children(0).add(new Node(9))
    root.children(0).children(0).add(new Node(10))
    root.children(2).children(0).add(new Node(11))
    root.children(2).children(0).add(new Node(12))
    root
  }

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

  "BFS" should "work" in {
    println(bfs(List(tree())))
  }
}

เอาท์พุท:

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

2

นี่คือการใช้งานหลาม:

graph = {'A': ['B', 'C'],
         'B': ['C', 'D'],
         'C': ['D'],
         'D': ['C'],
         'E': ['F'],
         'F': ['C']}

def bfs(paths, goal):
    if not paths:
        raise StopIteration

    new_paths = []
    for path in paths:
        if path[-1] == goal:
            yield path

        last = path[-1]
        for neighbor in graph[last]:
            if neighbor not in path:
                new_paths.append(path + [neighbor])
    yield from bfs(new_paths, goal)


for path in bfs([['A']], 'D'):
    print(path)

2

นี่คือการดำเนินการ Scala 2.11.4 ของ BFS แบบเรียกซ้ำ ฉันเสียสละการเพิ่มประสิทธิภาพการโทรแบบย่อ ๆ เพื่อความกะทัดรัด แต่รุ่น TCOd คล้ายกันมาก ดูโพสต์ของ@snvด้วย

import scala.collection.immutable.Queue

object RecursiveBfs {
  def bfs[A](tree: Tree[A], target: A): Boolean = {
    bfs(Queue(tree), target)
  }

  private def bfs[A](forest: Queue[Tree[A]], target: A): Boolean = {
    forest.dequeueOption exists {
      case (E, tail) => bfs(tail, target)
      case (Node(value, _, _), _) if value == target => true
      case (Node(_, l, r), tail) => bfs(tail.enqueue(List(l, r)), target)
    }
  }

  sealed trait Tree[+A]
  case class Node[+A](data: A, left: Tree[A], right: Tree[A]) extends Tree[A]
  case object E extends Tree[Nothing]
}

2

ต่อไปนี้ดูเป็นเรื่องธรรมชาติสำหรับฉันโดยใช้ Haskell ทำซ้ำวนซ้ำในระดับของต้นไม้ (ที่นี่ฉันรวบรวมชื่อเป็นสตริงคำสั่งขนาดใหญ่เพื่อแสดงเส้นทางผ่านต้นไม้):

data Node = Node {name :: String, children :: [Node]}
aTree = Node "r" [Node "c1" [Node "gc1" [Node "ggc1" []], Node "gc2" []] , Node "c2" [Node "gc3" []], Node "c3" [] ]
breadthFirstOrder x = levelRecurser [x]
    where levelRecurser level = if length level == 0
                                then ""
                                else concat [name node ++ " " | node <- level] ++ levelRecurser (concat [children node | node <- level])

2

นี่คือการประยุกต์ใช้ Python แบบสำรวจเส้นทางแบบเรียกซ้ำแบบ BFS ซึ่งทำงานกับกราฟที่ไม่มีวัฏจักร

def bfs_recursive(level):
    '''
     @params level: List<Node> containing the node for a specific level.
    '''
    next_level = []
    for node in level:
        print(node.value)
        for child_node in node.adjency_list:
            next_level.append(child_node)
    if len(next_level) != 0:
        bfs_recursive(next_level)


class Node:
    def __init__(self, value):
        self.value = value
        self.adjency_list = []

2

ฉันต้องการเพิ่มเซนต์ของฉันไปที่คำตอบสูงสุดในกรณีที่หากภาษารองรับสิ่งที่สร้างเช่นเครื่องกำเนิดไฟฟ้า, bfs สามารถทำร่วมกันซ้ำ

ในการเริ่มต้นด้วย @ คำตอบของ Tanzelax จะอ่าน:

การสำรวจเส้นทางแบบกว้างแรกนั้นใช้คิวไม่ใช่สแต็ก ลักษณะของคิวและสแต็กนั้นค่อนข้างตรงกันข้ามดังนั้นพยายามใช้ call stack (ซึ่งเป็น stack ดังนั้นชื่อ) เนื่องจากหน่วยเก็บข้อมูลสำรอง (คิว) นั้นค่อนข้างจะล้มเหลว

แน่นอนการเรียกใช้ฟังก์ชันทั่วไปจะไม่ทำงานเหมือนกองซ้อนปกติ แต่ฟังก์ชั่นเครื่องกำเนิดไฟฟ้าจะหยุดการทำงานของฟังก์ชั่นเพื่อให้เรามีโอกาสที่จะให้ลูกของโหนดในระดับต่อไปโดยไม่ต้องเจาะลึกลงไปในลูกหลานของโหนด

รหัสต่อไปนี้คือbfs ซ้ำใน Python

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

สัญชาตญาณที่นี่คือ:

  1. bfs ก่อนจะส่งคืนรูตตามผลลัพธ์แรก
  2. สมมติว่าเรามีลำดับ bfs อยู่แล้วระดับถัดไปขององค์ประกอบใน bfs คือชายด์ทันทีของโหนดก่อนหน้าในลำดับ
  3. ทำซ้ำสองขั้นตอนข้างต้น

ฉันไม่รู้จัก Python แต่ฉันคิดว่ารหัสของคุณแปลเป็นรหัสC #นี้ มันทำการสำรวจเส้นทาง BFS แต่ล้มเหลวด้วยข้อยกเว้น stackoverflow ฉันยังไม่เข้าใจสาเหตุที่ทำให้ อย่างไรก็ตามฉันปรับเปลี่ยนอัลกอริทึมเพื่อให้หยุด (และอาจมีประสิทธิภาพดีกว่า) คุณจะพบตัวอย่างการทำงานของฉันที่นี่
Adam Simon

1

ฉันต้องใช้ heap traversal ซึ่งให้ผลลัพธ์ในคำสั่ง BFS มันไม่ใช่ BFS จริง ๆ แต่ทำภารกิจเดียวกันให้สำเร็จ

private void getNodeValue(Node node, int index, int[] array) {
    array[index] = node.value;
    index = (index*2)+1;

    Node left = node.leftNode;
    if (left!=null) getNodeValue(left,index,array);
    Node right = node.rightNode;
    if (right!=null) getNodeValue(right,index+1,array);
}

public int[] getHeap() {
    int[] nodes = new int[size];
    getNodeValue(root,0,nodes);
    return nodes;
}

2
สำหรับผู้ดูอื่น ๆ : นี่เป็นตัวอย่างของการนำต้นไม้ที่สมบูรณ์ไปใช้ในอาร์เรย์ โดยเฉพาะ @Justin กำลังทำการสำรวจเส้นทางล่วงหน้าในระหว่างที่เขาบันทึกค่าโหนด (ตามลำดับ BFS) ในอาร์เรย์ที่ดัชนี BFS ที่เหมาะสม ฟังก์ชันนี้อนุญาตให้ฟังก์ชันการเรียกซ้ำแบบวนซ้ำผ่านอาร์เรย์โดยพิมพ์ค่าตามลำดับ BFS ดูคำอธิบายทั่วไปนี้หมายเหตุ: ฟังก์ชั่นการโทรจะต้องจัดการกับกรณีของต้นไม้ที่ไม่สมบูรณ์
bunkerdive

1

ให้ v เป็นจุดเริ่มต้น

ให้ G เป็นกราฟที่เป็นปัญหา

ต่อไปนี้เป็นรหัสเทียมโดยไม่ต้องใช้คิว

Initially label v as visited as you start from v
BFS(G,v)
    for all adjacent vertices w of v in G:
        if vertex w is not visited:
            label w as visited
    for all adjacent vertices w of v in G:
        recursively call BFS(G,w)

ฉันคิดว่าสิ่งนี้อาจติดอยู่ในวงวนไม่สิ้นสุด - จุดยอดกำลังถูกทำเครื่องหมายว่าเยี่ยมชม แต่พวกเขาไม่เคยทดสอบการเยี่ยมชมก่อนที่จะเกิดขึ้นอีกครั้ง

ตัวอย่างนี้คล้ายกับ DFS มากกว่า BFS
2560

1

BFS สำหรับต้นไม้ไบนารี (หรือ n-ary) สามารถทำซ้ำได้โดยไม่ต้องรอคิวดังต่อไปนี้ (ที่นี่ใน Java):

public class BreathFirst {

    static class Node {
        Node(int value) {
            this(value, 0);
        }
        Node(int value, int nChildren) {
            this.value = value;
            this.children = new Node[nChildren];
        }
        int value;
        Node[] children;
    }

    static void breathFirst(Node root, Consumer<? super Node> printer) {
        boolean keepGoing = true;
        for (int level = 0; keepGoing; level++) {
            keepGoing = breathFirst(root, printer, level);
        }
    }

    static boolean breathFirst(Node node, Consumer<? super Node> printer, int depth) {
        if (depth < 0 || node == null) return false;
        if (depth == 0) {
            printer.accept(node);
            return true;
        }
        boolean any = false;
        for (final Node child : node.children) {
            any |= breathFirst(child, printer, depth - 1);
        }
        return any;
    }
}

ตัวอย่างการพิมพ์ผ่านหมายเลข 1-12 ในลำดับจากน้อยไปหามาก:

public static void main(String... args) {
    //            1
    //          / | \
    //        2   3   4
    //      / |       | \
    //    5   6       7  8
    //  / |           | \
    // 9  10         11  12

    Node root = new Node(1, 3);
    root.children[0] = new Node(2, 2);
    root.children[1] = new Node(3);
    root.children[2] = new Node(4, 2);
    root.children[0].children[0] = new Node(5, 2);
    root.children[0].children[1] = new Node(6);
    root.children[2].children[0] = new Node(7, 2);
    root.children[2].children[1] = new Node(8);
    root.children[0].children[0].children[0] = new Node(9);
    root.children[0].children[0].children[1] = new Node(10);
    root.children[2].children[0].children[0] = new Node(11);
    root.children[2].children[0].children[1] = new Node(12);

    breathFirst(root, n -> System.out.println(n.value));
}

0
#include <bits/stdc++.h>
using namespace std;
#define Max 1000

vector <int> adj[Max];
bool visited[Max];

void bfs_recursion_utils(queue<int>& Q) {
    while(!Q.empty()) {
        int u = Q.front();
        visited[u] = true;
        cout << u << endl;
        Q.pop();
        for(int i = 0; i < (int)adj[u].size(); ++i) {
            int v = adj[u][i];
            if(!visited[v])
                Q.push(v), visited[v] = true;
        }
        bfs_recursion_utils(Q);
    }
}

void bfs_recursion(int source, queue <int>& Q) {
    memset(visited, false, sizeof visited);
    Q.push(source);
    bfs_recursion_utils(Q);
}

int main(void) {
    queue <int> Q;
    adj[1].push_back(2);
    adj[1].push_back(3);
    adj[1].push_back(4);

    adj[2].push_back(5);
    adj[2].push_back(6);

    adj[3].push_back(7);

    bfs_recursion(1, Q);
    return 0;
}

0

นี่คือการใช้งานจาวาสคริปต์ที่ทำให้เส้นทางการค้นหาเส้นทางที่กว้างและมีการวนซ้ำเป็นครั้งแรก ฉันกำลังเก็บค่าโหนดที่ความลึกแต่ละค่าภายในอาร์เรย์ภายในแฮช หากระดับนั้นมีอยู่แล้ว (เรามีการชนกันของข้อมูล) ดังนั้นเราแค่กดไปที่อาร์เรย์ในระดับนั้น คุณสามารถใช้อาร์เรย์แทนวัตถุ JavaScript ได้เช่นกันเนื่องจากระดับของเราเป็นตัวเลขและสามารถใช้เป็นดัชนีอาร์เรย์ได้ คุณสามารถคืนค่าโหนดค่าแปลงเป็นรายการที่เชื่อมโยงหรืออะไรก็ได้ที่คุณต้องการ ฉันแค่คืนค่าเพื่อความเรียบง่าย

BinarySearchTree.prototype.breadthFirstRec = function() {

    var levels = {};

    var traverse = function(current, depth) {
        if (!current) return null;
        if (!levels[depth]) levels[depth] = [current.value];
        else levels[depth].push(current.value);
        traverse(current.left, depth + 1);
        traverse(current.right, depth + 1);
    };

    traverse(this.root, 0);
    return levels;
};


var bst = new BinarySearchTree();
bst.add(20, 22, 8, 4, 12, 10, 14, 24);
console.log('Recursive Breadth First: ', bst.breadthFirstRec());
/*Recursive Breadth First:  
{ '0': [ 20 ],
  '1': [ 8, 22 ],
  '2': [ 4, 12, 24 ],
  '3': [ 10, 14 ] } */

นี่คือตัวอย่างของการสำรวจเส้นทางแรกของความกว้างที่แท้จริงโดยใช้วิธีการวนซ้ำ

BinarySearchTree.prototype.breadthFirst = function() {

    var result = '',
        queue = [],
        current = this.root;

    if (!current) return null;
    queue.push(current);

    while (current = queue.shift()) {
        result += current.value + ' ';
        current.left && queue.push(current.left);
        current.right && queue.push(current.right);
    }
    return result;
};

console.log('Breadth First: ', bst.breadthFirst());
//Breadth First:  20 8 22 4 12 24 10 14

0

ต่อไปนี้เป็นรหัสของฉันสำหรับการใช้งานแบบเรียกซ้ำแบบเต็มความกว้าง - การค้นหาครั้งแรกของกราฟแบบสองทิศทางโดยไม่ต้องใช้ลูปและคิว

public class Graph { public int V; public LinkedList<Integer> adj[]; Graph(int v) { V = v; adj = new LinkedList[v]; for (int i=0; i<v; ++i) adj[i] = new LinkedList<>(); } void addEdge(int v,int w) { adj[v].add(w); adj[w].add(v); } public LinkedList<Integer> getAdjVerted(int vertex) { return adj[vertex]; } public String toString() { String s = ""; for (int i=0;i<adj.length;i++) { s = s +"\n"+i +"-->"+ adj[i] ; } return s; } } //BFS IMPLEMENTATION public static void recursiveBFS(Graph graph, int vertex,boolean visited[], boolean isAdjPrinted[]) { if (!visited[vertex]) { System.out.print(vertex +" "); visited[vertex] = true; } if(!isAdjPrinted[vertex]) { isAdjPrinted[vertex] = true; List<Integer> adjList = graph.getAdjVerted(vertex); printAdjecent(graph, adjList, visited, 0,isAdjPrinted); } } public static void recursiveBFS(Graph graph, List<Integer> vertexList, boolean visited[], int i, boolean isAdjPrinted[]) { if (i < vertexList.size()) { recursiveBFS(graph, vertexList.get(i), visited, isAdjPrinted); recursiveBFS(graph, vertexList, visited, i+1, isAdjPrinted); } } public static void printAdjecent(Graph graph, List<Integer> list, boolean visited[], int i, boolean isAdjPrinted[]) { if (i < list.size()) { if (!visited[list.get(i)]) { System.out.print(list.get(i)+" "); visited[list.get(i)] = true; } printAdjecent(graph, list, visited, i+1, isAdjPrinted); } else { recursiveBFS(graph, list, visited, 0, isAdjPrinted); } }


0

การใช้ C # ของอัลกอริทึมการค้นหาแบบกว้างแรกแบบเรียกซ้ำสำหรับต้นไม้ไบนารี

การสร้างภาพข้อมูลต้นไม้ไบนารี

IDictionary<string, string[]> graph = new Dictionary<string, string[]> {
    {"A", new [] {"B", "C"}},
    {"B", new [] {"D", "E"}},
    {"C", new [] {"F", "G"}},
    {"E", new [] {"H"}}
};

void Main()
{
    var pathFound = BreadthFirstSearch("A", "H", new string[0]);
    Console.WriteLine(pathFound); // [A, B, E, H]

    var pathNotFound = BreadthFirstSearch("A", "Z", new string[0]);
    Console.WriteLine(pathNotFound); // []
}

IEnumerable<string> BreadthFirstSearch(string start, string end, IEnumerable<string> path)
{
    if (start == end)
    {
        return path.Concat(new[] { end });
    }

    if (!graph.ContainsKey(start)) { return new string[0]; }    

    return graph[start].SelectMany(letter => BreadthFirstSearch(letter, end, path.Concat(new[] { start })));
}

หากคุณต้องการอัลกอริทึมให้ทำงานไม่เพียง แต่กับไบนารีทรี แต่ด้วยกราฟสิ่งที่สามารถมีโหนดสองโหนดขึ้นไปที่ชี้ไปยังโหนดเดียวกันอีกโหนดหนึ่งคุณต้องหลีกเลี่ยงการปั่นจักรยานด้วยตนเองโดยถือรายการของโหนดที่เยี่ยมชมแล้ว การใช้งานอาจมีลักษณะเช่นนี้

การสร้างภาพข้อมูลกราฟ

IDictionary<string, string[]> graph = new Dictionary<string, string[]> {
    {"A", new [] {"B", "C"}},
    {"B", new [] {"D", "E"}},
    {"C", new [] {"F", "G", "E"}},
    {"E", new [] {"H"}}
};

void Main()
{
    var pathFound = BreadthFirstSearch("A", "H", new string[0], new List<string>());
    Console.WriteLine(pathFound); // [A, B, E, H]

    var pathNotFound = BreadthFirstSearch("A", "Z", new string[0], new List<string>());
    Console.WriteLine(pathNotFound); // []
}

IEnumerable<string> BreadthFirstSearch(string start, string end, IEnumerable<string> path, IList<string> visited)
{
    if (start == end)
    {
        return path.Concat(new[] { end });
    }

    if (!graph.ContainsKey(start)) { return new string[0]; }


    return graph[start].Aggregate(new string[0], (acc, letter) =>
    {
        if (visited.Contains(letter))
        {
            return acc;
        }

        visited.Add(letter);

        var result = BreadthFirstSearch(letter, end, path.Concat(new[] { start }), visited);
        return acc.Concat(result).ToArray();
    });
}

0

ฉันได้ทำโปรแกรมโดยใช้ c ++ ซึ่งทำงานในข้อต่อและกราฟแยกกัน

    #include <queue>
#include "iostream"
#include "vector"
#include "queue"

using namespace std;

struct Edge {
    int source,destination;
};

class Graph{
    int V;
    vector<vector<int>> adjList;
public:

    Graph(vector<Edge> edges,int V){
        this->V = V;
        adjList.resize(V);
        for(auto i : edges){
            adjList[i.source].push_back(i.destination);
            //     adjList[i.destination].push_back(i.source);
        }
    }
    void BFSRecursivelyJoinandDisjointtGraphUtil(vector<bool> &discovered, queue<int> &q);
    void BFSRecursivelyJointandDisjointGraph(int s);
    void printGraph();


};

void Graph :: printGraph()
{
    for (int i = 0; i < this->adjList.size(); i++)
    {
        cout << i << " -- ";
        for (int v : this->adjList[i])
            cout <<"->"<< v << " ";
        cout << endl;
    }
}


void Graph ::BFSRecursivelyJoinandDisjointtGraphUtil(vector<bool> &discovered, queue<int> &q) {
    if (q.empty())
        return;
    int v = q.front();
    q.pop();
    cout << v <<" ";
    for (int u : this->adjList[v])
    {
        if (!discovered[u])
        {
            discovered[u] = true;
            q.push(u);
        }
    }
    BFSRecursivelyJoinandDisjointtGraphUtil(discovered, q);

}

void Graph ::BFSRecursivelyJointandDisjointGraph(int s) {
    vector<bool> discovered(V, false);
    queue<int> q;

    for (int i = s; i < V; i++) {
        if (discovered[i] == false)
        {
            discovered[i] = true;
            q.push(i);
            BFSRecursivelyJoinandDisjointtGraphUtil(discovered, q);
        }
    }
}

int main()
{

    vector<Edge> edges =
            {
                    {0, 1}, {0, 2}, {1, 2}, {2, 0}, {2,3},{3,3}
            };

    int V = 4;
    Graph graph(edges, V);
 //   graph.printGraph();
    graph.BFSRecursivelyJointandDisjointGraph(2);
    cout << "\n";




    edges = {
            {0,4},{1,2},{1,3},{1,4},{2,3},{3,4}
    };

    Graph graph2(edges,5);

    graph2.BFSRecursivelyJointandDisjointGraph(0);
    return 0;
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.