Szukam nierekurencyjnego algorytmu pierwszego wyszukiwania głębokości dla drzewa niebinarnego. Wszelaka pomoc jest bardzo doceniana.
Szukam nierekurencyjnego algorytmu pierwszego wyszukiwania głębokości dla drzewa niebinarnego. Wszelaka pomoc jest bardzo doceniana.
Odpowiedzi:
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
}
Symetria tych dwóch jest całkiem fajna.
Aktualizacja: jak wskazano, take_first()usuwa i zwraca pierwszy element z listy.
.first()funkcja usuwa również element z listy. Jak shift()w wielu językach. pop()działa również i zwraca węzły potomne w kolejności od prawej do lewej zamiast od lewej do prawej.
gray(1st)->gray(2nd)->gray(3rd)->blacken(3rd)->blacken(2nd)->blacken(1st). Ale kod produkuje: gray(1st)->gray(2nd)->gray(3rd)->blacken(2nd)->blacken(3rd)->blacken(1st).
Użyłbyś stosu, który zawiera węzły, które nie były jeszcze odwiedzane:
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)aby ocenić, czy jest odpowiedni do umieszczenia na stosie. Czy to zadziała?
doing cycles? Myślę, że chcę tylko kolejność DFS. Czy to prawda, czy nie, dziękuję.
for each node.childNodes.reverse() do stack.push(stack) endfor). Prawdopodobnie tego też chcesz. Ładne wyjaśnienie, dlaczego tak jest, jest w tym filmie: youtube.com/watch?v=cZPXfl_tUkA endfor
Jeśli masz wskaźniki do węzłów nadrzędnych, możesz to zrobić bez dodatkowej pamięci.
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
Zwróć uwagę, że jeśli węzły potomne są przechowywane jako tablica, a nie za pomocą wskaźników rodzeństwa, następny element siostrzany można znaleźć jako:
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:.
Użyj stosu, aby śledzić swoje węzły
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)
}
Chociaż „użyj stosu” może działać jako odpowiedź na wymyślone pytanie do wywiadu, w rzeczywistości robi to wyraźnie, co program rekurencyjny robi za kulisami.
Rekursja korzysta z wbudowanego stosu programów. Kiedy wywołujesz funkcję, wypycha ona argumenty funkcji na stos, a gdy funkcja zwraca, robi to, zdejmując stos programu.
Implementacja ES6 oparta na świetnej odpowiedzi 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);
}
}
Ogólna logika jest taka, że wepchnij węzeł (zaczynając od katalogu głównego) do wartości Stack, Pop () it i Print (). Następnie, jeśli ma dzieci (lewe i prawe), wepchnij je do stosu - najpierw naciśnij Prawo, aby najpierw odwiedzić Lewe dziecko (po odwiedzeniu samego węzła). Gdy stos jest pusty (), odwiedzisz wszystkie węzły w przedsprzedaży.
Nierekurencyjny system plików DFS przy użyciu generatorów 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
}
}
Odchodzi od typowego nierekurencyjnego systemu plików DFS, aby łatwo wykryć, kiedy wszystkie osiągalne elementy potomne danego węzła zostały przetworzone i zachować bieżącą ścieżkę na liście / stosie.
Załóżmy, że chcesz wykonać powiadomienie, gdy odwiedzany jest każdy węzeł na wykresie. Prosta rekurencyjna implementacja to:
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, teraz potrzebujesz implementacji opartej na stosie, ponieważ Twój przykład nie działa. Złożone wykresy mogą na przykład spowodować wysadzenie stosu twojego programu i musisz zaimplementować wersję nierekurencyjną. Największym problemem jest wiedzieć, kiedy wysłać powiadomienie.
Działa następujący pseudokod (mieszanka Java i C ++ dla czytelności):
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);
}
}
Wygląda na skomplikowane, ale dodatkowa logika potrzebna do wysyłania powiadomień istnieje, ponieważ musisz powiadamiać w odwrotnej kolejności odwiedzin - DFS uruchamia się u roota, ale powiadamia go na końcu, w przeciwieństwie do BFS, który jest bardzo prosty do wdrożenia.
W przypadku kopnięć spróbuj następującego wykresu: węzły to s, t, v i w. skierowane krawędzie to: s-> t, s-> v, t-> w, v-> w i v-> t. Uruchom własną implementację DFS i kolejność, w której węzły powinny być odwiedzane, musi być następująca: w, t, v, s Nieporadna implementacja DFS może najpierw powiadomić t, a to wskazuje na błąd. Rekurencyjna implementacja DFS zawsze osiągnęłaby ostatni.
PEŁNY przykładowy kod ROBOCZY, bez stosu:
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);
}}
dane wyjściowe: Szerokość pierwszego przejścia - zaczynając od wierzchołka 2: 2 0 3 1 4 Głębokość pierwszego przejścia - zaczynając od wierzchołka 2: 2 3 4 1 0
Możesz użyć stosu. Zaimplementowałem wykresy z 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();
}
}
}
Iteracja DFS w Javie:
//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
Właśnie obejrzałem ten film i wyszedłem z implementacją. Wydaje mi się, że łatwo to zrozumieć. Proszę, skrytykuj to.
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()
}
Używając Stack, oto kroki, które należy wykonać: Wepchnij pierwszy wierzchołek stosu, a następnie
Oto program Java wykonujący powyższe kroki:
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);
}
}
Pseudokod oparty na odpowiedzi @ biziclop:
getNode(id)igetChildren(id)NUWAGA: używam indeksowania tablic od 1, a nie od 0.
Wszerz
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
Najpierw głębia
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
Tutaj jest link do programu java pokazującego DFS stosujący zarówno metody rekurencyjne, jak i nierekurencyjne, a także obliczający czas wykrywania i zakończenia , ale bez lalkowania na krawędzi.
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);
}
}
}
}
}
Pełne źródło tutaj .
Chciałem tylko dodać moją implementację Pythona do długiej listy rozwiązań. Ten nierekurencyjny algorytm wykrył i zakończył zdarzenia.
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)