algorithm - grafos - busqueda por profundidad en java
Algoritmo de primera búsqueda de profundidad no recursiva (14)
DFS iterativo 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;
}
Estoy buscando un primer algoritmo de búsqueda de profundidad no recursiva para un árbol no binario. Cualquier ayuda es muy apreciada.
DFS no recursivo usando generadores 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
}
}
Se desvía del típico DFS no recursivo para detectar fácilmente cuándo se procesaron todos los descendientes alcanzables de un nodo determinado y para mantener la ruta actual en la lista / pila.
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 simetría de los dos es bastante buena.
Actualización: Como se señaló, take_first()
elimina y devuelve el primer elemento de la lista.
Puedes usar una pila. Implementé gráficos con 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();
}
}
}
Si bien "usar una pila" podría funcionar como la respuesta a una pregunta artificial de entrevista, en realidad, es solo hacer explícitamente lo que hace un programa recursivo detrás de escena.
Recursion usa la pila integrada de programas. Cuando llama a una función, empuja los argumentos a la función en la pila y cuando la función regresa lo hace al abrir la pila de programas.
Si tiene punteros a nodos principales, puede hacerlo sin memoria adicional.
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
Tenga en cuenta que si los nodos secundarios se almacenan como una matriz en lugar de a través de punteros hermanos, el siguiente hermano se puede encontrar como:
def next_sibling(node):
try:
i = node.parent.child_nodes.index(node)
return node.parent.child_nodes[i+1]
except (IndexError, AttributeError):
return None
Supongamos que desea ejecutar una notificación cuando se visita cada nodo en un gráfico. La simple implementación recursiva es:
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, ahora quieres una implementación basada en pila porque tu ejemplo no funciona. Los gráficos complejos pueden, por ejemplo, hacer que esto arruine la pila de su programa y debe implementar una versión no recursiva. El mayor problema es saber cuándo emitir una notificación.
El siguiente pseudo-código funciona (mezcla de Java y C ++ para la legibilidad):
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);
}
}
Parece complicado, pero la lógica adicional necesaria para emitir notificaciones existe porque necesita notificar en el orden inverso de la visita: DFS se inicia en la raíz pero se lo notifica por última vez, a diferencia de BFS, que es muy fácil de implementar.
Para las patadas, intente seguir el gráfico: los nodos son s, t, v y w. los bordes dirigidos son: s-> t, s-> v, t-> w, v-> w, y v-> t. Ejecute su propia implementación de DFS y el orden en que se deben visitar los nodos debe ser: w, t, v, s Una implementación torpe del DFS podría notificar t primero y eso indica un error. Una implementación recursiva de DFS siempre llegaría a w last.
Una implementación de ES6 basada en la gran respuesta 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);
}
}
Usa una pila para rastrear tus nodos
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)
}
Usando Stack
, estos son los pasos a seguir: presione el primer vértice en la pila y luego,
- Si es posible, visite un vértice no visitado adyacente, márquelo e introdúzcalo en la pila.
- Si no puede seguir el paso 1, entonces, si es posible, saque un vértice de la pila.
- Si no puede seguir el paso 1 o el paso 2, ha terminado.
Aquí está el programa Java siguiendo los pasos anteriores:
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;
}
Utilizarías una stack que contiene los nodos que aún no se han visitado:
stack.push(root)
while !stack.isEmpty() do
node = stack.pop()
for each node.childNodes do
stack.push(stack)
endfor
// …
endwhile
http://www.youtube.com/watch?v=zLZhSSXAwxI
Acabo de ver este video y salió con la implementación. Me parece fácil de entender. Por favor critica esto.
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()
}
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);
}
}
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 lógica general es, insertar un nodo (comenzando desde la raíz) en el valor Pila, Pop () e Imprimir (). Luego, si tiene hijos (izquierda y derecha), empújelos a la pila: pulse Derecha primero para que vaya primero a la izquierda (después de visitar el nodo). Cuando la pila está vacía (), habrá visitado todos los nodos en Pre-Order.