Tôi đang tìm kiếm một thuật toán tìm kiếm đầu tiên chuyên sâu không đệ quy cho cây không nhị phân. Bất kỳ giúp đỡ được rất nhiều đánh giá cao.
Tôi đang tìm kiếm một thuật toán tìm kiếm đầu tiên chuyên sâu không đệ quy cho cây không nhị phân. Bất kỳ giúp đỡ được rất nhiều đánh giá cao.
Câu trả lời:
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
}
Sự đối xứng của hai người khá tuyệt.
Cập nhật: Như đã chỉ ra, take_first()
loại bỏ và trả về phần tử đầu tiên trong danh sách.
.first()
chức năng cũng loại bỏ phần tử khỏi danh sách. Giống như shift()
trong nhiều ngôn ngữ. pop()
cũng hoạt động và trả về các nút con theo thứ tự từ phải sang trái thay vì từ trái sang phải.
gray(1st)->gray(2nd)->gray(3rd)->blacken(3rd)->blacken(2nd)->blacken(1st)
. Nhưng mã của bạn tạo ra : gray(1st)->gray(2nd)->gray(3rd)->blacken(2nd)->blacken(3rd)->blacken(1st)
.
Bạn sẽ sử dụng ngăn xếp chứa các nút chưa được truy cập:
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)
để đánh giá xem nó có phù hợp để được đẩy vào ngăn xếp hay không. Có thể làm việc đó?
doing cycles
gì? Tôi nghĩ rằng tôi chỉ muốn thứ tự của DFS. Có đúng hay không, cảm ơn bạn.
for each node.childNodes.reverse() do stack.push(stack) endfor
). Đây cũng có thể là những gì bạn muốn. Lời giải thích tuyệt vời tại sao nó lại như vậy trong video này: youtube.com/watch?v=cZPXfl_tUkA endfor
Nếu bạn có con trỏ tới các nút cha, bạn có thể làm điều đó mà không cần thêm bộ nhớ.
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
Lưu ý rằng nếu các nút con được lưu trữ dưới dạng một mảng thay vì thông qua các con trỏ anh chị em, thì anh chị em tiếp theo có thể được tìm thấy như sau:
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:
.
Sử dụng ngăn xếp để theo dõi các nút của bạn
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)
}
Mặc dù "sử dụng một ngăn xếp" có thể hoạt động như câu trả lời cho câu hỏi phỏng vấn, nhưng thực tế, nó chỉ thực hiện rõ ràng những gì một chương trình đệ quy làm đằng sau hậu trường.
Đệ quy sử dụng ngăn xếp chương trình tích hợp. Khi bạn gọi một hàm, nó sẽ đẩy các đối số đến hàm lên ngăn xếp và khi hàm trả về, nó sẽ làm như vậy bằng cách bật ngăn xếp chương trình.
Một triển khai ES6 dựa trên câu trả lời tuyệt vời của 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);
}
}
Logic chung là, đẩy một nút (bắt đầu từ gốc) vào giá trị Stack, Pop () và Print (). Sau đó, nếu nó có con (trái và phải) đẩy chúng vào ngăn xếp - đẩy Phải trước để bạn sẽ đến thăm con trái trước (sau khi truy cập nút chính). Khi ngăn xếp trống () bạn sẽ truy cập tất cả các nút trong Đơn đặt hàng trước.
DFS không đệ quy sử dụng bộ tạo 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
}
}
Nó đi chệch khỏi DFS không đệ quy điển hình để dễ dàng phát hiện khi tất cả các hậu duệ có thể tiếp cận của nút đã cho được xử lý và để duy trì đường dẫn hiện tại trong danh sách / ngăn xếp.
Giả sử bạn muốn thực thi một thông báo khi mỗi nút trong biểu đồ được truy cập. Việc thực hiện đệ quy đơn giản là:
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
}
Ok, bây giờ bạn muốn triển khai dựa trên ngăn xếp vì ví dụ của bạn không hoạt động. Ví dụ, các biểu đồ phức tạp có thể khiến điều này làm nổ tung chương trình của bạn và bạn cần triển khai một phiên bản không đệ quy. Vấn đề lớn nhất là biết khi nào nên đưa ra thông báo.
Các mã giả sau đây hoạt động (kết hợp Java và C ++ để dễ đọ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);
}
}
Trông có vẻ phức tạp nhưng logic bổ sung cần thiết để phát hành thông báo tồn tại bởi vì bạn cần thông báo theo thứ tự truy cập ngược - DFS bắt đầu từ root nhưng thông báo lần cuối, không giống như BFS rất đơn giản để thực hiện.
Đối với các cú đá, hãy thử biểu đồ sau: các nút là s, t, v và w. các cạnh được định hướng là: s-> t, s-> v, t-> w, v-> w và v-> t. Chạy triển khai DFS của riêng bạn và thứ tự các nút nên được truy cập phải là: w, t, v, s Việc triển khai DFS vụng về có thể thông báo cho t trước và điều đó cho thấy có lỗi. Việc triển khai đệ quy DFS sẽ luôn đạt đến w cuối cùng.
Ví dụ đầy đủ Mã làm việc, không có ngăn xếp:
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);
}}
đầu ra: Breadth First Traversal- bắt đầu từ đỉnh 2: 2 0 3 1 4 Độ sâu Traversal First- bắt đầu từ đỉnh 2: 2 3 4 1 0
Bạn có thể sử dụng một ngăn xếp. Tôi đã triển khai các biểu đồ với Ma trận điều chỉnh:
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();
}
}
}
Lặp lại DFS trong 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
Chỉ cần xem video này và đi ra với thực hiện. Có vẻ dễ hiểu cho tôi. Hãy phê bình điều này.
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()
}
Sử dụng Stack
, đây là các bước để làm theo: Đẩy đỉnh đầu tiên trên ngăn xếp sau đó,
Đây là chương trình Java theo các bước trên:
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);
}
}
Mã giả dựa trên câu trả lời của @ biziclop:
getNode(id)
vàgetChildren(id)
N
LƯU Ý: Tôi sử dụng lập chỉ mục mảng từ 1, không phải 0.
Bề rộng đầu tiên
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
Chiều sâu trước
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
Đây là một liên kết đến một chương trình java hiển thị DFS theo cả hai phương pháp reccursive và không reccursive và cũng tính toán thời gian khám phá và kết thúc , nhưng không có laleling cạnh.
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);
}
}
}
}
}
Nguồn đầy đủ ở đây .
Chỉ muốn thêm thực hiện python của tôi vào danh sách dài các giải pháp. Thuật toán không đệ quy này đã phát hiện và kết thúc các sự kiện.
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)