web-dev-qa-db-fra.com

Premier algorithme de recherche de profondeur non récursif

Je recherche un premier algorithme de recherche de profondeur non-récursif pour un arbre non-binaire. Toute aide est fortement appréciée.

142
mousey

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.

264
biziclop

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
33
Gumbo

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
28
aaz

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)

}
5
corsiKa

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.

3
Chris Bennet

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);
  }
}

3
Max Leizerovich
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.

2
Sanj

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.

1
Jarek Przygódzki

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.

1
user3804051

En utilisant Stack, voici les étapes à suivre: Poussez le premier sommet de la pile puis,

  1. Si possible, visitez un sommet non visité adjacent, marquez-le, .__ et enfoncez-le dans la pile.
  2. Si vous ne pouvez pas suivre l’étape 1, alors, si possible, supprimez un sommet de la pile
  3. Si vous ne pouvez pas suivre les étapes 1 ou 2, vous avez terminé.

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;
}
0
        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);
            }
        }
0
iMobaio

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();
        }
    }
}
0
noDispName

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

0
Assaf Faybish

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;
}
0
Piyush Patel

Pseudo-code basé sur la réponse de @ biziclop:

  • Utiliser uniquement des constructions de base: variables, tableaux, si, tant que et pour
  • Fonctions getNode(id) et getChildren(id)
  • En supposant un nombre connu de nœuds 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
0
Sheljohn

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()
}
0
prog_guy