ฉันกำลังมองหาอัลกอริทึมการค้นหาที่ไม่ต้องเรียกซ้ำความลึกแรกสำหรับต้นไม้ที่ไม่ใช่ไบนารี ความช่วยเหลือใด ๆ ที่ชื่นชมมาก
ฉันกำลังมองหาอัลกอริทึมการค้นหาที่ไม่ต้องเรียกซ้ำความลึกแรกสำหรับต้นไม้ที่ไม่ใช่ไบนารี ความช่วยเหลือใด ๆ ที่ชื่นชมมาก
คำตอบ:
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()
ลบและส่งคืนองค์ประกอบแรกในรายการ
.first()
ฟังก์ชั่นยังลบองค์ประกอบจากรายการ ชอบshift()
หลายภาษา pop()
ใช้งานได้และส่งคืนโหนดชายด์ในลำดับจากขวาไปซ้ายแทนซ้ายไปขวา
gray(1st)->gray(2nd)->gray(3rd)->blacken(3rd)->blacken(2nd)->blacken(1st)
ความคืบหน้าควรจะ: gray(1st)->gray(2nd)->gray(3rd)->blacken(2nd)->blacken(3rd)->blacken(1st)
แต่รหัสของคุณผลิต:
คุณจะใช้สแต็กที่เก็บโหนดที่ยังไม่ได้เยี่ยมชม:
stack.push(root)
while !stack.isEmpty() do
node = stack.pop()
for each node.childNodes do
stack.push(stack)
endfor
// …
endwhile
if (nodes are not marked)
ตัดสินว่ามันจะถูกผลักไปที่กองซ้อนหรือไม่ สามารถทำงานได้
doing cycles
อะไร? ฉันคิดว่าฉันแค่ต้องการคำสั่งของ DFS ถูกต้องหรือไม่ขอบคุณ
for each node.childNodes.reverse() do stack.push(stack) endfor
) นี่อาจเป็นสิ่งที่คุณต้องการ คำอธิบายที่ดีว่าทำไมมันเป็นอย่างนั้นในวิดีโอนี้: youtube.com/watch?v=cZPXfl_tUkA endfor
หากคุณมีตัวชี้ไปยังโหนดพาเรนต์คุณสามารถทำได้โดยไม่มีหน่วยความจำเพิ่มเติม
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
while not node.next_sibling or node is root:
ง่าย
ใช้สแต็กเพื่อติดตามโหนดของคุณ
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)
}
ในขณะที่ "ใช้สแต็ค" อาจใช้เป็นคำตอบสำหรับคำถามสัมภาษณ์ที่วางแผนไว้ แต่ในความเป็นจริงมันเป็นเพียงการทำสิ่งที่โปรแกรมเรียกซ้ำทำอยู่เบื้องหลัง
การเรียกซ้ำใช้โปรแกรมในตัวสแต็ก เมื่อคุณเรียกใช้ฟังก์ชันฟังก์ชันจะส่งอาร์กิวเมนต์ไปยังฟังก์ชันลงในสแต็กและเมื่อฟังก์ชันส่งคืนฟังก์ชันนั้นโดยการเรียกโปรแกรมสแต็ก
การติดตั้ง 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);
}
}
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);
}
}
ตรรกะทั่วไปคือกดโหนด (เริ่มจากรูท) ลงในสแต็กป๊อป () มันและค่าพิมพ์ () จากนั้นถ้ามีลูก (ซ้ายและขวา) ดันเข้าไปในกอง - กดขวาก่อนเพื่อให้คุณไปที่ลูกซ้ายก่อน (หลังจากไปที่โหนดเอง) เมื่อสแต็กว่างเปล่า () คุณจะได้เยี่ยมชมโหนดทั้งหมดในการสั่งซื้อล่วงหน้า
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 ที่ไม่ใช่แบบเรียกซ้ำโดยทั่วไปเพื่อตรวจจับได้ง่ายเมื่อลูกหลานที่เข้าถึงได้ทั้งหมดของโหนดที่กำหนดได้รับการประมวลผลและเพื่อรักษาเส้นทางปัจจุบันในรายการ / สแต็ก
สมมติว่าคุณต้องการดำเนินการแจ้งเตือนเมื่อมีการเยี่ยมชมแต่ละโหนดในกราฟ การใช้งานแบบเรียกซ้ำง่าย ๆ คือ:
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 ล่าสุดเสมอ
ตัวอย่างรหัสการทำงานทั้งหมดโดยไม่มีสแต็ก:
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
คุณสามารถใช้สแต็ค ฉันใช้กราฟด้วย 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();
}
}
}
ทำซ้ำ 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;
}
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()
}
ใช้ต่อStack
ไปนี้เป็นขั้นตอนในการปฏิบัติดังนี้กดจุดสุดยอดแรกบนสแต็กแล้ว
นี่คือโปรแกรม 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;
}
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);
}
}
หลอกรหัสตามคำตอบของ @ 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
นี่คือลิงค์ไปยังโปรแกรมจาวาที่แสดง 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);
}
}
}
}
}
เพียงแค่ต้องการเพิ่มการติดตั้ง 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)