Je recherche un premier algorithme de recherche en profondeur non récursif pour un arbre non binaire. Toute aide est fortement appréciée.
Je recherche un premier algorithme de recherche en profondeur non récursif pour un arbre non binaire. Toute aide est fortement appréciée.
Réponses:
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
}
La symétrie des deux est assez cool.
Mise à jour: comme indiqué, take_first()
supprime et renvoie le premier élément de la liste.
.first()
fonction supprime également l'élément de la liste. Comme shift()
dans de nombreuses langues. pop()
fonctionne également et renvoie les nœuds enfants dans l'ordre de droite à gauche au lieu de gauche à droite.
gray(1st)->gray(2nd)->gray(3rd)->blacken(3rd)->blacken(2nd)->blacken(1st)
. Mais votre code produit: gray(1st)->gray(2nd)->gray(3rd)->blacken(2nd)->blacken(3rd)->blacken(1st)
.
Vous utiliseriez une pile contenant les nœuds qui n'ont pas encore été visités:
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)
pour juger s'il est approprié d'être poussé vers la pile. Cela peut-il fonctionner?
doing cycles
? Je pense que je veux juste l'ordre de DFS. Est-ce vrai ou pas, merci.
for each node.childNodes.reverse() do stack.push(stack) endfor
). C'est aussi probablement ce que vous voulez. Belle explication pourquoi c'est comme ça dans cette vidéo: youtube.com/watch?v=cZPXfl_tUkA endfor
Si vous avez des pointeurs vers des nœuds parents, vous pouvez le faire sans mémoire supplémentaire.
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
Notez que si les nœuds enfants sont stockés sous forme de tableau plutôt que via des pointeurs frères, le frère suivant peut être trouvé sous la forme:
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:
.
Utilisez une pile pour suivre vos nœuds
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)
}
Bien que «utiliser une pile» puisse fonctionner comme la réponse à une question d'interview artificielle, en réalité, il s'agit simplement de faire explicitement ce qu'un programme récursif fait dans les coulisses.
La récursivité utilise la pile intégrée des programmes. Lorsque vous appelez une fonction, il pousse les arguments de la fonction sur la pile et lorsque la fonction retourne, il le fait en sautant la pile de programmes.
Une implémentation ES6 basée sur une excellente réponse 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);
}
}
La logique générale est de pousser un nœud (à partir de la racine) dans la valeur Stack, Pop () it et Print (). Ensuite, s'il a des enfants (gauche et droite), poussez-les dans la pile - appuyez d'abord sur Droite pour que vous visitiez d'abord Left child (après avoir visité le nœud lui-même). Lorsque la pile est vide (), vous aurez visité tous les nœuds en pré-commande.
DFS non récursif utilisant des générateurs 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
}
}
Il s'écarte du DFS non récursif typique pour détecter facilement quand tous les descendants joignables d'un nœud donné ont été traités et pour maintenir le chemin actuel dans la liste / pile.
Supposons que vous souhaitiez exécuter une notification lorsque chaque nœud d'un graphique est visité. L'implémentation récursive simple est:
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, maintenant vous voulez une implémentation basée sur la pile car votre exemple ne fonctionne pas. Des graphes complexes peuvent par exemple faire exploser la pile de votre programme et vous devez implémenter une version non récursive. Le plus gros problème est de savoir quand émettre une notification.
Le pseudo-code suivant fonctionne (mélange de Java et C ++ pour la lisibilité):
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);
}
}
Cela semble compliqué mais la logique supplémentaire nécessaire pour émettre des notifications existe car vous devez notifier dans l'ordre inverse de la visite - DFS commence à la racine mais le notifie en dernier, contrairement à BFS qui est très simple à implémenter.
Pour les coups de pied, essayez le graphique suivant: les nœuds sont s, t, v et w. les arêtes dirigées sont: s-> t, s-> v, t-> w, v-> w et v-> t. Exécutez votre propre implémentation de DFS et l'ordre dans lequel les nœuds doivent être visités doit être: w, t, v, s Une implémentation maladroite de DFS pourrait peut-être notifier t d'abord et cela indique un bogue. Une implémentation récursive de DFS atteindrait toujours w en dernier.
Exemple complet de code de travail, sans pile:
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);
}}
sortie: Largeur première traversée - à partir du sommet 2: 2 0 3 1 4 Profondeur première traversée - à partir du sommet 2: 2 3 4 1 0
Vous pouvez utiliser une pile. J'ai implémenté des graphiques avec 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();
}
}
}
Itératif DFS en 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
Je viens de regarder cette vidéo et est sorti avec la mise en œuvre. Cela me semble facile à comprendre. Veuillez critiquer cela.
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()
}
En utilisant Stack
, voici les étapes à suivre: Poussez le premier sommet de la pile puis,
Voici le programme Java en suivant les étapes ci-dessus:
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);
}
}
Pseudo-code basé sur la réponse de @ biziclop:
getNode(id)
etgetChildren(id)
N
REMARQUE: j'utilise l'indexation de tableau à partir de 1, pas de 0.
Largeur d'abord
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
La profondeur d'abord
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
Voici un lien vers un programme java montrant DFS suivant à la fois des méthodes récursives et non récursives et calculant également l' heure de découverte et de fin , mais pas de laleling des bords.
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);
}
}
}
}
}
Source complète ici .
Je voulais juste ajouter mon implémentation python à la longue liste de solutions. Cet algorithme non récursif a des événements de découverte et terminés.
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)