Je recherche un premier algorithme de recherche de profondeur non-récursif pour un arbre non-binaire. Toute aide est fortement appréciée.
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.
Vous utiliseriez un stack qui contient les noeuds non encore visités:
stack.Push(root)
while !stack.isEmpty() do
node = stack.pop()
for each node.childNodes do
stack.Push(stack)
endfor
// …
endwhile
Si vous avez des pointeurs sur les 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é comme suit:
def next_sibling(node):
try:
i = node.parent.child_nodes.index(node)
return node.parent.child_nodes[i+1]
except (IndexError, AttributeError):
return None
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)
}
Alors que "utiliser une pile" pourrait fonctionnerait comme réponse à une question d’interview inventée, en réalité, il s’agit de faire explicitement ce qu’un programme récursif fait en coulisse.
La récursivité utilise la pile intégrée aux 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 vidant la pile du programme.
Une implémentation ES6 basée sur la bonne réponse de 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 la suivante: Insérez un nœud (à partir de la racine) dans la pile, Pop () et la valeur Print (). Puis s'il a des enfants (à gauche et à droite) Poussez-les dans la pile - Poussez tout d'abord vers la droite pour que vous visitiez d'abord l'enfant de gauche (après avoir visité le noeud 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 de DFS non récursif typique pour détecter facilement quand tous les descendants accessibles d'un noeud donné ont été traités et pour conserver 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 une pile car votre exemple ne fonctionne pas. Des graphiques 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 envoyer une notification.
Le pseudo-code suivant fonctionne (mélange de Java et de 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 parce que vous devez notifier dans l'ordre inverse des visites: DFS commence à la racine mais le notifie en dernier, contrairement à BFS qui est très simple à mettre en œuvre.
Pour les coups de pied, essayez le graphique suivant: Noeuds sont s, t, v et w . Arêtes dirigées sont: S-> t, s-> v, t-> w, v-> w, et v-> t . Lancez votre propre implémentation de DFS et l'ordre dans lequel les noeuds doivent être visités doit être:. et cela indique un bug. Une implémentation récursive de DFS atteindrait toujours w en dernier.
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);
}
}
Vous pouvez utiliser une pile. J'ai implémenté des graphes 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();
}
}
}
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
DFS itératif 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;
}
Pseudo-code basé sur la réponse de @ biziclop:
getNode(id)
et getChildren(id)
N
NOTE: J'utilise l'indexation de tableau à partir de 1, pas 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
Profondeur
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
http://www.youtube.com/watch?v=zLZhSSXAwxI
Vient de regarder cette vidéo et est sorti avec la mise en œuvre. Cela me semble facile à comprendre. S'il vous plaît 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()
}