อัลกอริธึมการค้นหาแรกที่ไม่ต้องเรียกซ้ำ


173

ฉันกำลังมองหาอัลกอริทึมการค้นหาที่ไม่ต้องเรียกซ้ำความลึกแรกสำหรับต้นไม้ที่ไม่ใช่ไบนารี ความช่วยเหลือใด ๆ ที่ชื่นชมมาก


1
@Bart Kiers ต้นไม้โดยทั่วไปตัดสินโดยแท็ก
biziclop

13
การค้นหาความลึกครั้งแรกเป็นอัลกอริทึมแบบเรียกซ้ำ คำตอบด้านล่างเป็นการสำรวจโหนดซ้ำ ๆ พวกเขาไม่ได้ใช้ call stack ของระบบเพื่อทำการเรียกซ้ำและใช้ stack stack อย่างชัดเจน
Null ตั้งค่า

8
@Null Set No มันเป็นเพียงลูป ตามคำจำกัดความของคุณทุกโปรแกรมคอมพิวเตอร์จะเรียกซ้ำ (ซึ่งในความหมายบางอย่างของคำที่พวกเขาเป็น.)
biziclop

1
@Null Set: ต้นไม้เป็นโครงสร้างข้อมูลแบบเรียกซ้ำ
Gumbo

2
@MuhammadUmer ประโยชน์หลักของการทำซ้ำในแบบวนซ้ำเมื่อพิจารณาซ้ำจะอ่านได้น้อยกว่าคือคุณสามารถหลีกเลี่ยงข้อ จำกัด เชิงลึกขนาดสแต็ก / การเรียกซ้ำสูงสุดที่ระบบ / ภาษาโปรแกรมส่วนใหญ่ใช้เพื่อปกป้องสแต็ก ด้วยหน่วยความจำแบบสแต็กของคุณสแต็กของคุณจะถูก จำกัด ด้วยจำนวนหน่วยความจำที่โปรแกรมของคุณอนุญาตให้ใช้ซึ่งโดยทั่วไปจะอนุญาตสแต็กที่มีขนาดใหญ่กว่าขนาดสแต็กการโทรสูงสุด
John B

คำตอบ:


313

DFS:

list nodes_to_visit = {root};
while( nodes_to_visit isn't empty ) {
  currentnode = nodes_to_visit.take_first();
  nodes_to_visit.prepend( currentnode.children );
  //do something
}

BFS:

list nodes_to_visit = {root};
while( nodes_to_visit isn't empty ) {
  currentnode = nodes_to_visit.take_first();
  nodes_to_visit.append( currentnode.children );
  //do something
}

สมมาตรของทั้งสองค่อนข้างเย็น

อัปเดต:ตามที่อธิบายแล้วให้take_first()ลบและส่งคืนองค์ประกอบแรกในรายการ


11
+1 สำหรับการสังเกตความคล้ายคลึงกันของทั้งสองเมื่อดำเนินการแบบเรียกซ้ำ (ราวกับว่าพวกเขาแตกต่างอย่างสิ้นเชิงเมื่อพวกเขาซ้ำ แต่ยัง ... )
corsiKa

3
จากนั้นเพื่อเพิ่มความสมมาตรหากคุณใช้ลำดับความสำคัญขั้นต่ำเป็นขอบแทนคุณมีตัวค้นหาเส้นทางที่สั้นที่สุดที่มา
Mark Peters

10
BTW .first()ฟังก์ชั่นยังลบองค์ประกอบจากรายการ ชอบshift()หลายภาษา pop()ใช้งานได้และส่งคืนโหนดชายด์ในลำดับจากขวาไปซ้ายแทนซ้ายไปขวา
Ariel

5
IMO อัลโก DFS ไม่ถูกต้องเล็กน้อย ลองนึกภาพ 3 จุดเชื่อมต่อทั้งหมดซึ่งกันและกัน gray(1st)->gray(2nd)->gray(3rd)->blacken(3rd)->blacken(2nd)->blacken(1st)ความคืบหน้าควรจะ: gray(1st)->gray(2nd)->gray(3rd)->blacken(2nd)->blacken(3rd)->blacken(1st)แต่รหัสของคุณผลิต:
แบทแมน

3
@ learner ฉันอาจเข้าใจผิดตัวอย่างของคุณ แต่ถ้าพวกเขาเชื่อมต่อกันมันไม่ได้เป็นต้นไม้จริงๆ
biziclop

40

คุณจะใช้สแต็กที่เก็บโหนดที่ยังไม่ได้เยี่ยมชม:

stack.push(root)
while !stack.isEmpty() do
    node = stack.pop()
    for each node.childNodes do
        stack.push(stack)
    endfor
    // …
endwhile

2
@Gumbo ฉันสงสัยว่าถ้ามันเป็นกราฟที่มี cycyles ใช้งานได้ไหม ฉันคิดว่าฉันสามารถหลีกเลี่ยงการเพิ่มโหนด dulplicated ในสแต็กและสามารถทำงานได้ สิ่งที่ฉันจะทำคือการทำเครื่องหมายเพื่อนบ้านทั้งหมดของโหนดที่โผล่ออกมาและเพิ่มการif (nodes are not marked)ตัดสินว่ามันจะถูกผลักไปที่กองซ้อนหรือไม่ สามารถทำงานได้
Alston

1
@Stallman คุณสามารถจดจำโหนดที่คุณเคยเยี่ยมชมแล้ว หากคุณเข้าชมเฉพาะโหนดที่คุณยังไม่เคยเข้าชมคุณจะไม่ดำเนินการใด ๆ
Gumbo

@Gumbo คุณหมายถึงdoing cyclesอะไร? ฉันคิดว่าฉันแค่ต้องการคำสั่งของ DFS ถูกต้องหรือไม่ขอบคุณ
Alston

แค่ต้องการชี้ให้เห็นว่าการใช้สแต็ก (LIFO) หมายถึงการสำรวจเส้นทางแรกที่มีความลึก หากคุณต้องการใช้ความกว้างก่อนไปด้วยคิว (FIFO) แทน
ต่อ Lundberg

3
เป็นที่น่าสังเกตว่าการมีรหัสเทียบเท่าเป็นคำตอบ @biziclop ที่ได้รับความนิยมมากที่สุดคุณจะต้องกดบันทึกย่อของลูกในลำดับย้อนกลับ ( for each node.childNodes.reverse() do stack.push(stack) endfor) นี่อาจเป็นสิ่งที่คุณต้องการ คำอธิบายที่ดีว่าทำไมมันเป็นอย่างนั้นในวิดีโอนี้: youtube.com/watch?v=cZPXfl_tUkA endfor
Mariusz Pawelski

32

หากคุณมีตัวชี้ไปยังโหนดพาเรนต์คุณสามารถทำได้โดยไม่มีหน่วยความจำเพิ่มเติม

def dfs(root):
    node = root
    while True:
        visit(node)
        if node.first_child:
            node = node.first_child      # walk down
        else:
            while not node.next_sibling:
                if node is root:
                    return
                node = node.parent       # walk up ...
            node = node.next_sibling     # ... and right

โปรดทราบว่าถ้าโหนดลูกถูกเก็บไว้เป็นอาร์เรย์แทนที่จะผ่านพอยน์เตอร์พอยน์เตอร์จะพบ sibling ถัดไปดังนี้:

def next_sibling(node):
    try:
        i =    node.parent.child_nodes.index(node)
        return node.parent.child_nodes[i+1]
    except (IndexError, AttributeError):
        return None

นี่เป็นวิธีแก้ปัญหาที่ดีเพราะไม่ได้ใช้หน่วยความจำเพิ่มเติมหรือการจัดการรายการหรือสแต็ก (เหตุผลที่ดีบางประการเพื่อหลีกเลี่ยงการเรียกซ้ำ) อย่างไรก็ตามเป็นไปได้ก็ต่อเมื่อโหนดต้นไม้มีลิงก์ไปยังผู้ปกครอง
joeytwiddle

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

6
"หากคุณมีพอยน์เตอร์ไปยังโหนดพาเรนต์คุณสามารถทำได้โดยไม่มีหน่วยความจำเพิ่มเติม": การเก็บตัวชี้ไปยังโหนดพาเรนต์ใช้ "หน่วยความจำเพิ่มเติม" บางอย่าง ...
rptr

1
@ rptr87 หากยังไม่ชัดเจนไม่มีหน่วยความจำเพิ่มเติมนอกเหนือจากตัวชี้เหล่านั้น
Abhinav Gauniyal

สิ่งนี้จะล้มเหลวสำหรับต้นไม้บางส่วนที่โหนดไม่ได้เป็นรูทสัมบูรณ์ แต่สามารถแก้ไขได้โดยwhile not node.next_sibling or node is root:ง่าย
Basel Shishani

5

ใช้สแต็กเพื่อติดตามโหนดของคุณ

Stack<Node> s;

s.prepend(tree.head);

while(!s.empty) {
    Node n = s.poll_front // gets first node

    // do something with q?

    for each child of n: s.prepend(child)

}

1
@Dave O. ไม่เพราะคุณดันลูก ๆ ของโหนดที่เยี่ยมชมต่อหน้าทุกสิ่งที่มีอยู่แล้ว
biziclop

ฉันต้องตีความความหมายของpush_backแล้ว
เดฟทุม

@Dave คุณมีจุดดีมาก ฉันคิดว่ามันควรจะ "ผลักดันส่วนที่เหลือของคิวกลับ" ไม่ "ผลักไปด้านหลัง" ฉันจะแก้ไขอย่างเหมาะสม
corsiKa

หากคุณกำลังผลักไปข้างหน้ามันควรจะเป็นกอง
เที่ยวบิน

@ ทิมมีใช่ฉันไม่แน่ใจว่าฉันคิดอะไรอยู่ที่นั่น @quasiverse โดยปกติเราคิดว่าคิวเป็นคิว FIFO สแต็กถูกกำหนดเป็นคิว LIFO
corsiKa

4

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

การเรียกซ้ำใช้โปรแกรมในตัวสแต็ก เมื่อคุณเรียกใช้ฟังก์ชันฟังก์ชันจะส่งอาร์กิวเมนต์ไปยังฟังก์ชันลงในสแต็กและเมื่อฟังก์ชันส่งคืนฟังก์ชันนั้นโดยการเรียกโปรแกรมสแต็ก


7
ด้วยความแตกต่างที่สำคัญที่เธรดสแต็ก จำกัด อย่างรุนแรงและอัลกอริธึมที่ไม่เกิดซ้ำจะใช้ฮีปที่ปรับขนาดได้มากขึ้น
Yam Marcovic

1
นี่ไม่ใช่แค่สถานการณ์ที่วางแผนไว้ ฉันได้ใช้เทคนิคเช่นนี้ในบางโอกาสใน C # และ JavaScript เพื่อให้ได้ประสิทธิภาพที่เพิ่มขึ้นอย่างมีนัยสำคัญเมื่อเทียบกับ equivelants สายเรียกซ้ำ บ่อยครั้งที่การจัดการการเรียกซ้ำด้วยสแต็กแทนที่จะใช้ call stack นั้นเร็วกว่าและใช้ทรัพยากรน้อยลง มีค่าใช้จ่ายจำนวนมากที่เกี่ยวข้องกับการวางบริบทการโทรลงในสแต็กกับโปรแกรมเมอร์ที่สามารถตัดสินใจได้จริงเกี่ยวกับสิ่งที่จะวางบนสแต็กที่กำหนดเอง
Jason Jackson

4

การติดตั้ง ES6 ตามคำตอบที่ยอดเยี่ยมของ biziclops:

root = {
  text: "root",
  children: [{
    text: "c1",
    children: [{
      text: "c11"
    }, {
      text: "c12"
    }]
  }, {
    text: "c2",
    children: [{
      text: "c21"
    }, {
      text: "c22"
    }]
  }, ]
}

console.log("DFS:")
DFS(root, node => node.children, node => console.log(node.text));

console.log("BFS:")
BFS(root, node => node.children, node => console.log(node.text));

function BFS(root, getChildren, visit) {
  let nodesToVisit = [root];
  while (nodesToVisit.length > 0) {
    const currentNode = nodesToVisit.shift();
    nodesToVisit = [
      ...nodesToVisit,
      ...(getChildren(currentNode) || []),
    ];
    visit(currentNode);
  }
}

function DFS(root, getChildren, visit) {
  let nodesToVisit = [root];
  while (nodesToVisit.length > 0) {
    const currentNode = nodesToVisit.shift();
    nodesToVisit = [
      ...(getChildren(currentNode) || []),
      ...nodesToVisit,
    ];
    visit(currentNode);
  }
}


3
PreOrderTraversal is same as DFS in binary tree. You can do the same recursion 
taking care of Stack as below.

    public void IterativePreOrder(Tree root)
            {
                if (root == null)
                    return;
                Stack s<Tree> = new Stack<Tree>();
                s.Push(root);
                while (s.Count != 0)
                {
                    Tree b = s.Pop();
                    Console.Write(b.Data + " ");
                    if (b.Right != null)
                        s.Push(b.Right);
                    if (b.Left != null)
                        s.Push(b.Left);

                }
            }

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


2

DFS แบบไม่เรียกซ้ำโดยใช้ตัวกำเนิด ES6

class Node {
  constructor(name, childNodes) {
    this.name = name;
    this.childNodes = childNodes;
    this.visited = false;
  }
}

function *dfs(s) {
  let stack = [];
  stack.push(s);
  stackLoop: while (stack.length) {
    let u = stack[stack.length - 1]; // peek
    if (!u.visited) {
      u.visited = true; // grey - visited
      yield u;
    }

    for (let v of u.childNodes) {
      if (!v.visited) {
        stack.push(v);
        continue stackLoop;
      }
    }

    stack.pop(); // black - all reachable descendants were processed 
  }    
}

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


1

สมมติว่าคุณต้องการดำเนินการแจ้งเตือนเมื่อมีการเยี่ยมชมแต่ละโหนดในกราฟ การใช้งานแบบเรียกซ้ำง่าย ๆ คือ:

void DFSRecursive(Node n, Set<Node> visited) {
  visited.add(n);
  for (Node x : neighbors_of(n)) {  // iterate over all neighbors
    if (!visited.contains(x)) {
      DFSRecursive(x, visited);
    }
  }
  OnVisit(n);  // callback to say node is finally visited, after all its non-visited neighbors
}

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

โค้ดหลอกต่อไปนี้ใช้งานได้ (ผสมผสานระหว่าง Java และ C ++ เพื่อให้สามารถอ่านได้):

void DFS(Node root) {
  Set<Node> visited;
  Set<Node> toNotify;  // nodes we want to notify

  Stack<Node> stack;
  stack.add(root);
  toNotify.add(root);  // we won't pop nodes from this until DFS is done
  while (!stack.empty()) {
    Node current = stack.pop();
    visited.add(current);
    for (Node x : neighbors_of(current)) {
      if (!visited.contains(x)) {
        stack.add(x);
        toNotify.add(x);
      }
    }
  }
  // Now issue notifications. toNotifyStack might contain duplicates (will never
  // happen in a tree but easily happens in a graph)
  Set<Node> notified;
  while (!toNotify.empty()) {
  Node n = toNotify.pop();
  if (!toNotify.contains(n)) {
    OnVisit(n);  // issue callback
    toNotify.add(n);
  }
}

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

สำหรับ kicks ลองกราฟต่อไปนี้: nodes คือ s, t, v และ w ขอบกำกับคือ: s-> t, s-> v, t-> w, v-> w และ v-> t รันการใช้งาน DFS ของคุณเองและลำดับที่โหนดที่ควรเยี่ยมชมต้องเป็น: w, t, v, s การใช้งาน DFS ที่งุ่มง่ามอาจแจ้งให้ทราบก่อนและบ่งชี้ว่ามีข้อบกพร่อง การใช้งานซ้ำของ DFS จะถึง w ล่าสุดเสมอ


1

ตัวอย่างรหัสการทำงานทั้งหมดโดยไม่มีสแต็ก:

import java.util.*;

class Graph {
private List<List<Integer>> adj;

Graph(int numOfVertices) {
    this.adj = new ArrayList<>();
    for (int i = 0; i < numOfVertices; ++i)
        adj.add(i, new ArrayList<>());
}

void addEdge(int v, int w) {
    adj.get(v).add(w); // Add w to v's list.
}

void DFS(int v) {
    int nodesToVisitIndex = 0;
    List<Integer> nodesToVisit = new ArrayList<>();
    nodesToVisit.add(v);
    while (nodesToVisitIndex < nodesToVisit.size()) {
        Integer nextChild= nodesToVisit.get(nodesToVisitIndex++);// get the node and mark it as visited node by inc the index over the element.
        for (Integer s : adj.get(nextChild)) {
            if (!nodesToVisit.contains(s)) {
                nodesToVisit.add(nodesToVisitIndex, s);// add the node to the HEAD of the unvisited nodes list.
            }
        }
        System.out.println(nextChild);
    }
}

void BFS(int v) {
    int nodesToVisitIndex = 0;
    List<Integer> nodesToVisit = new ArrayList<>();
    nodesToVisit.add(v);
    while (nodesToVisitIndex < nodesToVisit.size()) {
        Integer nextChild= nodesToVisit.get(nodesToVisitIndex++);// get the node and mark it as visited node by inc the index over the element.
        for (Integer s : adj.get(nextChild)) {
            if (!nodesToVisit.contains(s)) {
                nodesToVisit.add(s);// add the node to the END of the unvisited node list.
            }
        }
        System.out.println(nextChild);
    }
}

public static void main(String args[]) {
    Graph g = new Graph(5);

    g.addEdge(0, 1);
    g.addEdge(0, 2);
    g.addEdge(1, 2);
    g.addEdge(2, 0);
    g.addEdge(2, 3);
    g.addEdge(3, 3);
    g.addEdge(3, 1);
    g.addEdge(3, 4);

    System.out.println("Breadth First Traversal- starting from vertex 2:");
    g.BFS(2);
    System.out.println("Depth First Traversal- starting from vertex 2:");
    g.DFS(2);
}}

เอาท์พุท: เส้นทางแรกกว้าง - เริ่มจากจุดสุดยอด 2: 2 0 3 1 4 ความลึกเส้นทางแรก - เริ่มจากจุดสุดยอด 2: 2 3 4 1 0


0

คุณสามารถใช้สแต็ค ฉันใช้กราฟด้วย Adjacency Matrix:

void DFS(int current){
    for(int i=1; i<N; i++) visit_table[i]=false;
    myStack.push(current);
    cout << current << "  ";
    while(!myStack.empty()){
        current = myStack.top();
        for(int i=0; i<N; i++){
            if(AdjMatrix[current][i] == 1){
                if(visit_table[i] == false){ 
                    myStack.push(i);
                    visit_table[i] = true;
                    cout << i << "  ";
                }
                break;
            }
            else if(!myStack.empty())
                myStack.pop();
        }
    }
}

0

ทำซ้ำ DFS ใน Java:

//DFS: Iterative
private Boolean DFSIterative(Node root, int target) {
    if (root == null)
        return false;
    Stack<Node> _stack = new Stack<Node>();
    _stack.push(root);
    while (_stack.size() > 0) {
        Node temp = _stack.peek();
        if (temp.data == target)
            return true;
        if (temp.left != null)
            _stack.push(temp.left);
        else if (temp.right != null)
            _stack.push(temp.right);
        else
            _stack.pop();
    }
    return false;
}

คำถามจะถามอย่างชัดเจนสำหรับต้นไม้ที่ไม่ใช่ไบนารี
user3743222

คุณต้องมีแผนที่เยี่ยมเพื่อหลีกเลี่ยงการวนซ้ำไม่ จำกัด
spiralmoon

0

http://www.youtube.com/watch?v=zLZhSSXAwxI

เพิ่งดูวิดีโอนี้และออกมาพร้อมกับการนำไปใช้งาน มันดูง่ายสำหรับฉันที่จะเข้าใจ โปรดวิจารณ์สิ่งนี้

visited_node={root}
stack.push(root)
while(!stack.empty){
  unvisited_node = get_unvisited_adj_nodes(stack.top());
  If (unvisited_node!=null){
     stack.push(unvisited_node);  
     visited_node+=unvisited_node;
  }
  else
     stack.pop()
}

0

ใช้ต่อStackไปนี้เป็นขั้นตอนในการปฏิบัติดังนี้กดจุดสุดยอดแรกบนสแต็กแล้ว

  1. ถ้าเป็นไปได้ให้ไปที่จุดสุดยอดที่ไม่เคยเยี่ยมชมทำเครื่องหมายและกดลงบนสแต็ก
  2. หากคุณไม่สามารถทำตามขั้นตอนที่ 1 ได้ถ้าเป็นไปได้ให้วางจุดยอดออกจากสแต็ก
  3. หากคุณไม่สามารถทำตามขั้นตอนที่ 1 หรือขั้นตอนที่ 2 แสดงว่าคุณทำเสร็จแล้ว

นี่คือโปรแกรม Java โดยทำตามขั้นตอนด้านบน:

public void searchDepthFirst() {
    // begin at vertex 0
    vertexList[0].wasVisited = true;
    displayVertex(0);
    stack.push(0);
    while (!stack.isEmpty()) {
        int adjacentVertex = getAdjacentUnvisitedVertex(stack.peek());
        // if no such vertex
        if (adjacentVertex == -1) {
            stack.pop();
        } else {
            vertexList[adjacentVertex].wasVisited = true;
            // Do something
            stack.push(adjacentVertex);
        }
    }
    // stack is empty, so we're done, reset flags
    for (int j = 0; j < nVerts; j++)
            vertexList[j].wasVisited = false;
}

0
        Stack<Node> stack = new Stack<>();
        stack.add(root);
        while (!stack.isEmpty()) {
            Node node = stack.pop();
            System.out.print(node.getData() + " ");

            Node right = node.getRight();
            if (right != null) {
                stack.push(right);
            }

            Node left = node.getLeft();
            if (left != null) {
                stack.push(left);
            }
        }

0

หลอกรหัสตามคำตอบของ @ biziclop:

  • ใช้โครงสร้างพื้นฐานเท่านั้น: ตัวแปรอาร์เรย์ถ้าในขณะที่และสำหรับ
  • ฟังก์ชั่นgetNode(id)และgetChildren(id)
  • สมมติว่ามีจำนวนโหนดที่รู้จักกัน N

หมายเหตุ: ฉันใช้การทำดัชนีอาร์เรย์จาก 1 ไม่ใช่ 0

กว้างแรก

S = Array(N)
S[1] = 1; // root id
cur = 1;
last = 1
while cur <= last
    id = S[cur]
    node = getNode(id)
    children = getChildren(id)

    n = length(children)
    for i = 1..n
        S[ last+i ] = children[i]
    end
    last = last+n
    cur = cur+1

    visit(node)
end

ความลึกแรก

S = Array(N)
S[1] = 1; // root id
cur = 1;
while cur > 0
    id = S[cur]
    node = getNode(id)
    children = getChildren(id)

    n = length(children)
    for i = 1..n
        // assuming children are given left-to-right
        S[ cur+i-1 ] = children[ n-i+1 ] 

        // otherwise
        // S[ cur+i-1 ] = children[i] 
    end
    cur = cur+n-1

    visit(node)
end

0

นี่คือลิงค์ไปยังโปรแกรมจาวาที่แสดง DFS ตามวิธีการทั้งแบบ reccursive และ non-reccursive และการคำนวณการค้นพบและเวลาสิ้นสุดแต่ไม่มี edge laleling

    public void DFSIterative() {
    Reset();
    Stack<Vertex> s = new Stack<>();
    for (Vertex v : vertices.values()) {
        if (!v.visited) {
            v.d = ++time;
            v.visited = true;
            s.push(v);
            while (!s.isEmpty()) {
                Vertex u = s.peek();
                s.pop();
                boolean bFinished = true;
                for (Vertex w : u.adj) {
                    if (!w.visited) {
                        w.visited = true;
                        w.d = ++time;
                        w.p = u;
                        s.push(w);
                        bFinished = false;
                        break;
                    }
                }
                if (bFinished) {
                    u.f = ++time;
                    if (u.p != null)
                        s.push(u.p);
                }
            }
        }
    }
}

มาเต็มรูปแบบที่นี่


0

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


worklist = [root_node]
visited = set()
while worklist:
    node = worklist[-1]
    if node in visited:
        # Node is finished
        worklist.pop()
    else:
        # Node is discovered
        visited.add(node)
        for child in node.children:
            worklist.append(child)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.