Supongamos que desea implementar una búsqueda amplia de un árbol binario de forma recursiva . ¿Cómo lo harías?
¿Es posible usar solo la pila de llamadas como almacenamiento auxiliar?
Supongamos que desea implementar una búsqueda amplia de un árbol binario de forma recursiva . ¿Cómo lo harías?
¿Es posible usar solo la pila de llamadas como almacenamiento auxiliar?
Respuestas:
(Supongo que esto es solo un tipo de ejercicio de pensamiento, o incluso una pregunta de tarea / entrevista engañosa, pero supongo que podría imaginar un escenario extraño en el que no se le permite ningún espacio de almacenamiento dinámico por alguna razón [alguna muy mala costumbre administrador de memoria? algunos problemas extraños de tiempo de ejecución / sistema operativo?] mientras todavía tiene acceso a la pila ...)
El primer recorrido de ancho usa tradicionalmente una cola, no una pila. La naturaleza de una cola y una pila son bastante opuestas, por lo que tratar de usar la pila de llamadas (que es una pila, de ahí el nombre) como almacenamiento auxiliar (una cola) está prácticamente condenada al fracaso, a menos que esté haciendo algo estúpidamente ridículo con la pila de llamadas que no deberías ser.
En el mismo token, la naturaleza de cualquier recursión sin cola que intente implementar es esencialmente agregar una pila al algoritmo. Esto hace que ya no se amplíe la primera búsqueda en un árbol binario y, por lo tanto, el tiempo de ejecución y otras cosas para BFS tradicional ya no se aplican por completo. Por supuesto, siempre puede convertir trivialmente cualquier bucle en una llamada recursiva, pero eso no es ningún tipo de recursión significativa.
Sin embargo, hay formas, como lo demuestran otros, para implementar algo que sigue la semántica de BFS a un costo. Si el costo de la comparación es costoso pero el recorrido del nodo es barato, entonces, como lo hizo @Simon Buchan , simplemente puede ejecutar una búsqueda iterativa en profundidad, solo procesando las hojas. Esto significaría que no hay una cola creciente almacenada en el montón, solo una variable de profundidad local, y las pilas que se acumulan una y otra vez en la pila de llamadas a medida que se recorre el árbol una y otra vez. Y como señaló @Patrick , un árbol binario respaldado por una matriz generalmente se almacena en orden transversal primero, por lo que una búsqueda de primer orden sería trivial, también sin necesidad de una cola auxiliar.
fuente
Si usa una matriz para respaldar el árbol binario, puede determinar el siguiente nodo algebraicamente. si
i
es un nodo, sus hijos se pueden encontrar en2i + 1
(para el nodo izquierdo) y2i + 2
(para el nodo derecho). El siguiente vecino de un nodo está dado pori + 1
, a menos quei
sea un poder de2
Aquí hay un pseudocódigo para una implementación muy ingenua de la primera búsqueda de amplitud en un árbol de búsqueda binaria respaldado por matriz. Esto supone una matriz de tamaño fijo y, por lo tanto, un árbol de profundidad fija. Examinará los nodos sin padres y podría crear una pila inmanejablemente grande.
fuente
No pude encontrar una manera de hacerlo completamente recursivo (sin ninguna estructura de datos auxiliar). Pero si la cola Q se pasa por referencia, entonces puede tener la siguiente función recursiva de cola tonta:
fuente
El siguiente método usó un algoritmo DFS para obtener todos los nodos en una profundidad particular, que es lo mismo que hacer BFS para ese nivel. Si descubre la profundidad del árbol y hace esto para todos los niveles, los resultados serán los mismos que los de un BFS.
Encontrar la profundidad de un árbol es pan comido:
fuente
level
sea cero.Una simple recursión BFS y DFS en Java:
simplemente presione / ofrezca el nodo raíz del árbol en la pila / cola y llame a estas funciones.
fuente
Encontré un algoritmo muy hermoso recursivo (incluso funcional) de Breadth-First transversal. No es mi idea, pero creo que debería mencionarse en este tema.
Chris Okasaki explica su algoritmo de numeración más amplio de ICFP 2000 en http://okasaki.blogspot.de/2008/07/breadth-first-numbering-algorithm-in.html muy claramente con solo 3 imágenes.
La implementación Scala de Debasish Ghosh, que encontré en http://debasishg.blogspot.de/2008/09/breadth-first-numbering-okasakis.html , es:
fuente
La manera tonta:
fuente
Aquí hay una breve solución de Scala :
La idea de utilizar el valor de retorno como acumulador es muy adecuada. Se puede implementar en otros idiomas de manera similar, solo asegúrese de que su función recursiva procese la lista de nodos .
Lista de códigos de prueba (usando el árbol de prueba @marco):
Salida:
fuente
Aquí hay una implementación de Python:
fuente
Aquí hay una implementación de Scala 2.11.4 de BFS recursivo. He sacrificado la optimización de llamadas de cola por brevedad, pero la versión TCOd es muy similar. Ver también la publicación de @snv .
fuente
Lo siguiente me parece bastante natural, usando Haskell. Iterar recursivamente sobre los niveles del árbol (aquí recopilo nombres en una gran cadena ordenada para mostrar la ruta a través del árbol):
fuente
Aquí hay una implementación de Python transversal recursiva de BFS, trabajando para un gráfico sin ciclo.
fuente
Me gustaría agregar mis centavos a la respuesta principal en que si el lenguaje admite algo como generador, bfs se puede hacer de forma recursiva.
Para empezar, la respuesta de @ Tanzelax dice:
De hecho, la pila de llamadas de función ordinarias no se comportará como una pila normal. Pero la función de generador suspenderá la ejecución de la función, por lo que nos da la oportunidad de producir el siguiente nivel de hijos de nodos sin profundizar en los descendientes más profundos del nodo.
El siguiente código es bfs recursivo en Python.
La intuición aquí es:
fuente
Tuve que implementar un recorrido del montón que sale en un orden BFS. En realidad no es BFS, pero realiza la misma tarea.
fuente
Sea v el vértice inicial
Deje G ser el gráfico en cuestión
El siguiente es el pseudocódigo sin usar cola
fuente
BFS para un árbol binario (o n-ario) se puede hacer de forma recursiva sin colas de la siguiente manera (aquí en Java):
Un ejemplo de impresión transversal números 1-12 en orden ascendente:
fuente
fuente
Aquí hay una implementación de JavaScript que simula Breadth First Traversal con Depth First recursión. Estoy almacenando los valores de nodo en cada profundidad dentro de una matriz, dentro de un hash. Si ya existe un nivel (tenemos una colisión), entonces simplemente avanzamos a la matriz en ese nivel. También podría usar una matriz en lugar de un objeto JavaScript, ya que nuestros niveles son numéricos y pueden servir como índices de matriz. Puede devolver nodos, valores, convertirlos en una lista vinculada o lo que desee. Solo estoy devolviendo valores en aras de la simplicidad.
Aquí hay un ejemplo del recorrido transversal de Breadth First utilizando un enfoque iterativo.
fuente
El siguiente es mi código para la implementación completamente recursiva de la búsqueda de amplitud en primer plano de un gráfico bidireccional sin usar bucle y cola.
fuente
Implementación de C # del algoritmo de búsqueda de amplitud recursiva para un árbol binario.
Visualización de datos de árbol binario.
Si desea que el algoritmo funcione no solo con un árbol binario, sino también con gráficos, lo que puede tener dos y más nodos que apuntan al mismo nodo, debe evitar el ciclo automático manteniendo una lista de nodos ya visitados. La implementación puede ser así.
Visualización de datos gráficos
fuente
Hice un programa usando c ++ que también funciona en gráficos conjuntos y disjuntos.
fuente