Búsqueda en profundidad primero


La búsqueda en profundidad ( DFS ) es un algoritmo para recorrer o buscar estructuras de datos de árbol o gráfico . El algoritmo comienza en el nodo raíz (seleccionando algún nodo arbitrario como nodo raíz en el caso de un gráfico) y explora en la medida de lo posible a lo largo de cada rama antes de retroceder.

Una versión de la búsqueda en profundidad fue investigada en el siglo XIX por el matemático francés Charles Pierre Trémaux [1] como estrategia para resolver laberintos . [2] [3]

El análisis de tiempo y espacio de DFS difiere según su área de aplicación. En informática teórica, DFS se utiliza normalmente para recorrer un gráfico completo y lleva tiempo, [4] lineal en el tamaño del gráfico. En estas aplicaciones también utiliza el espacio.en el peor de los casos, para almacenar la pila de vértices en la ruta de búsqueda actual, así como el conjunto de vértices ya visitados. Por lo tanto, en esta configuración, los límites de tiempo y espacio son los mismos que para la búsqueda de amplitud primero y la elección de cuál de estos dos algoritmos usar depende menos de su complejidad y más de las diferentes propiedades de los ordenamientos de vértices que producen los dos algoritmos. .

Para aplicaciones de DFS en relación con dominios específicos, como la búsqueda de soluciones en inteligencia artificial o rastreo web, el gráfico que se va a recorrer es a menudo demasiado grande para visitarlo en su totalidad o infinito (DFS puede sufrir de no terminación ). En tales casos, la búsqueda solo se realiza a una profundidad limitada ; debido a los recursos limitados, como la memoria o el espacio en disco, normalmente no se utilizan estructuras de datos para realizar un seguimiento del conjunto de todos los vértices visitados anteriormente. Cuando la búsqueda se realiza a una profundidad limitada, el tiempo sigue siendo lineal en términos del número de vértices y aristas expandidos (aunque este número no es el mismo que el tamaño de todo el gráfico porque algunos vértices se pueden buscar más de una vez y otros en absoluto), pero la complejidad del espacio de esta variante de DFS es solo proporcional al límite de profundidad y, como resultado, es mucho más pequeño que el espacio necesario para buscar a la misma profundidad utilizando la búsqueda primero en amplitud. Para tales aplicaciones, DFS también se presta mucho mejor a métodos heurísticos para elegir una rama de apariencia probable. Cuando no se conoce a priori un límite de profundidad apropiado, la búsqueda iterativa de profundización primero aplica DFS repetidamente con una secuencia de límites crecientes. En el modo de análisis de inteligencia artificial, con un factor de ramificación mayor que uno, la profundización iterativa aumenta el tiempo de ejecución solo en un factor constante sobre el caso en el que se conoce el límite de profundidad correcto debido al crecimiento geométrico del número de nodos por nivel. .

DFS también se puede utilizar para recopilar una muestra de nodos de gráficos. Sin embargo, incompleta DFS, de manera similar a incompletas BFS , está sesgada hacia los nodos de alto grado .

Ejemplo animado de una búsqueda en profundidad

Para el siguiente gráfico:

An undirected graph with edges AB, BD, BF, FE, AC, CG, AE

una búsqueda en profundidad que comienza en el nodo A, asumiendo que los bordes izquierdos en el gráfico mostrado se eligen antes que los bordes derechos, y asumiendo que la búsqueda recuerda los nodos visitados anteriormente y no los repetirá (ya que este es un gráfico pequeño), visitará los nodos en el siguiente orden: A, B, D, F, E, C, G. Las aristas atravesadas en esta búsqueda forman un árbol de Trémaux , una estructura con importantes aplicaciones en la teoría de grafos . Realizar la misma búsqueda sin recordar los nodos visitados anteriormente da como resultado visitar los nodos en el orden A, B, D, F, E, A, B, D, F, E, etc. para siempre, atrapados en A, B, D, Ciclo F, E y nunca llegar a C o G.

La profundización iterativa es una técnica para evitar este bucle infinito y llegaría a todos los nodos.

Los cuatro tipos de bordes definidos por un árbol de expansión

Una descripción conveniente de una búsqueda en profundidad de un gráfico es en términos de un árbol de expansión de los vértices alcanzados durante la búsqueda. Con base en este árbol de expansión, los bordes del gráfico original se pueden dividir en tres clases: bordes delanteros , que apuntan desde un nodo del árbol a uno de sus descendientes, bordes posteriores , que apuntan desde un nodo a uno de sus antepasados, y bordes transversales , que no lo hacen. A veces, los bordes de los árboles , los bordes que pertenecen al árbol de expansión en sí, se clasifican por separado de los bordes delanteros. Si el gráfico original no está dirigido, todos sus bordes son bordes de árbol o bordes posteriores.

Pedido DFS

Se dice que una enumeración de los vértices de un gráfico es una ordenación DFS si es el resultado posible de la aplicación de DFS a este gráfico.

Dejar ser un gráfico con vértices. Para ser una lista de elementos distintos de , por , dejar ser el mejor tal que es vecino de , si tal existe y ser de lo contrario.

Dejar ser una enumeración de los vértices de . La enumeración se dice que es un pedido DFS (con fuente ) si, para todos , es el vértice tal que es máxima. Recordar que es el conjunto de vecinos de . Equivalentemente, es un pedido DFS si, para todos con , existe un vecino de tal que .

Ordenaciones de vértices

También es posible utilizar la búsqueda en profundidad para ordenar linealmente los vértices de un gráfico o árbol. Hay cuatro formas posibles de hacer esto:

  • Un preorden es una lista de los vértices en el orden en que fueron visitados por primera vez por el algoritmo de búsqueda en profundidad. Esta es una forma compacta y natural de describir el progreso de la búsqueda, como se hizo anteriormente en este artículo. Un preorden de un árbol de expresión es la expresión en notación polaca .
  • Un postordering es una lista de los vértices en el orden en que fueron visitados por última vez por el algoritmo. Una posordenación de un árbol de expresión es la expresión en notación polaca inversa .
  • Un preorden inverso es el inverso de un preorden, es decir, una lista de los vértices en el orden opuesto de su primera visita. El pedido anticipado inverso no es lo mismo que el pedido posterior.
  • Un postordering inverso es el reverso de un postordering, es decir, una lista de los vértices en el orden opuesto de su última visita. Postorder inverso no es lo mismo que preordenar.

En el caso de los árboles binarios, existe además el orden y el orden inverso .

Por ejemplo, cuando se busca en el gráfico dirigido a continuación comenzando en el nodo A, la secuencia de recorridos es ABDBACA o ACDCABA (elegir visitar primero B o C desde A depende del algoritmo). Tenga en cuenta que las visitas repetidas en forma de retroceso a un nodo, para comprobar si todavía tiene vecinos no visitados, se incluyen aquí (incluso si se encuentra que no tiene ninguno). Por lo tanto, los posibles pedidos anticipados son ABDC y ACDB, mientras que los posibles pedidos anticipados son DBCA y DCBA, y los posibles pedidos anticipados son ACBD y ABC D.

A directed graph with edges AB, BD, AC, CD

El posordenamiento inverso produce una clasificación topológica de cualquier gráfico acíclico dirigido . Este orden también es útil en el análisis de flujo de control, ya que a menudo representa una linealización natural de los flujos de control. El gráfico anterior puede representar el flujo de control en el fragmento de código a continuación, y es natural considerar este código en el orden ABCD o ACBD, pero no es natural usar el orden ABDC o ACD B.

si ( A ) entonces { B} demás { C}D

Entrada : un gráfico G y un vértice v de G

Salida : todos los vértices accesibles desde v etiquetados como descubiertos

Una implementación recursiva de DFS: [5]

procedimiento DFS ( G , v ) es la etiqueta v como descubrió para todos los bordes dirigidos de v a w que son  en  G .adjacentEdges ( v ) hacer  si vértice w no se etiqueta como descubierto luego recursivamente llamar DFS ( G , w )

El orden en que este algoritmo descubre los vértices se denomina orden lexicográfico . [ cita requerida ]

Una implementación no recursiva de DFS con la complejidad de espacio del peor de los casos , con posibilidad de vértices duplicados en la pila: [6]

procedimiento DFS_iterative ( G , v ) es vamos S ser una pila S .push ( v ) , mientras que  S no está vacío do  v = S .pop () si  v no está etiquetado como descubierto entonces etiqueta v como descubrió para todos los bordes de v a w  en  G .adjacentEdges ( v ) hacer  S .push ( w )
An undirected graph with edges AB, BD, BF, FE, AC, CG, AE

Estas dos variaciones de DFS visitan a los vecinos de cada vértice en el orden opuesto entre sí: el primer vecino de v visitado por la variación recursiva es el primero en la lista de bordes adyacentes, mientras que en la variación iterativa el primer vecino visitado es el último en la lista de bordes adyacentes. La implementación recursiva visitará los nodos del gráfico de ejemplo en el siguiente orden: A, B, D, F, E, C, G. La implementación no recursiva visitará los nodos como: A, E, F, B, D , C, G.

La implementación no recursiva es similar a la búsqueda en amplitud, pero se diferencia de ella de dos maneras:

  1. usa una pila en lugar de una cola, y
  2. retrasa la comprobación de si se ha descubierto un vértice hasta que el vértice se extrae de la pila en lugar de realizar esta comprobación antes de agregar el vértice.

Si G es un árbol , reemplazar la cola del algoritmo de búsqueda en amplitud con una pila producirá un algoritmo de búsqueda en profundidad. Para gráficos generales, reemplazar la pila de la implementación iterativa de búsqueda en profundidad primero con una cola también produciría un algoritmo de búsqueda en amplitud, aunque algo no estándar. [7]

Otra posible implementación de la búsqueda iterativa en profundidad utiliza una pila de iteradores de la lista de vecinos de un nodo, en lugar de una pila de nodos. Esto produce el mismo recorrido que el DFS recursivo. [8]

procedimiento DFS_iterative ( G , v ) es dejar que S sea ​​una pila S .push (iterador de G .adjectedEdges ( v )) mientras que  S no está vacío hacer  si  S .peek (). hasNext () entonces  w = S .peek () .next () si  w no está etiquetado como descubierto, entonces etiquete w como descubierto S .push (iterador de G .adjectedEdges ( w )) else  S .pop () 

Algoritmo aleatorio similar a la búsqueda en profundidad que se utiliza para generar un laberinto.

Los algoritmos que utilizan la búsqueda en profundidad como un bloque de construcción incluyen:

  • Encontrar componentes conectados .
  • Clasificación topológica .
  • Encontrar componentes conectados a 2 (borde o vértice).
  • Encontrar componentes conectados a 3 (borde o vértice).
  • Encontrar los puentes de una gráfica.
  • Generar palabras para trazar el conjunto límite de un grupo .
  • Encontrar componentes fuertemente conectados .
  • Determinar si una especie está más cerca de una especie u otra en un árbol filogenético.
  • Prueba de planaridad . [9] [10]
  • Resolver acertijos con una sola solución, como laberintos . (DFS se puede adaptar para encontrar todas las soluciones a un laberinto al incluir solo los nodos en la ruta actual en el conjunto visitado).
  • La generación de laberintos puede utilizar una búsqueda aleatoria en profundidad.
  • Encontrar biconnectividad en gráficos .

La complejidad computacional de la DFS fue investigado por John Reif . Más precisamente, dado un gráfico, dejar ser el orden calculado por el algoritmo DFS recursivo estándar. Este orden se denomina orden lexicográfico de búsqueda en profundidad primero. John Reif consideró la complejidad de calcular el orden lexicográfico de búsqueda en profundidad primero, dado un gráfico y una fuente. Una versión de decisión del problema (probar si algún vértice u ocurre antes de algún vértice v en este orden) es P -completa , [11] lo que significa que es "una pesadilla para el procesamiento paralelo ". [12] : 189

Un orden de búsqueda en profundidad (no necesariamente lexicográfico) puede calcularse mediante un algoritmo paralelo aleatorio en la clase de complejidad RNC . [13] En 1997, seguía sin conocerse si se podría construir un recorrido de profundidad primero mediante un algoritmo paralelo determinista, en la clase de complejidad NC . [14]

  • Recorrido del árbol (para obtener detalles sobre el recorrido en profundidad con prioridad al pedido previo, en orden y posterior al pedido)
  • Búsqueda en amplitud primero
  • Búsqueda iterativa que profundiza primero en profundidad
  • Buscar juegos

  1. Charles Pierre Trémaux (1859-1882) École polytechnique of Paris (X: 1876), ingeniero francés del telégrafo
    en una conferencia pública, 2 de diciembre de 2010 - por el profesor Jean Pelletier-Thibert en Académie de Macon (Borgoña - Francia) - ( Resumen publicado en el académico Annals, marzo de 2011 - ISSN  0980-6032 )
  2. ^ Even, Shimon (2011), Graph Algorithms (2.a ed.), Cambridge University Press, págs. 46–48, ISBN 978-0-521-73653-4.
  3. ^ Sedgewick, Robert (2002), Algoritmos en C ++: algoritmos de gráficos (3a ed.), Pearson Education, ISBN 978-0-201-36118-6.
  4. ^ Cormen, Thomas H., Charles E. Leiserson y Ronald L. Rivest. p.606
  5. ^ Goodrich y Tamassia; Cormen, Leiserson, Rivest y Stein
  6. ^ Página 93, Diseño de algoritmos, Kleinberg y Tardos
  7. ^ "Recorrido de gráfico basado en pila ≠ primera búsqueda en profundidad" . 11011110.github.io . Consultado el 10 de junio de 2020 .
  8. ^ Sedgewick, Robert (2010). Algoritmos en Java . Addison-Wesley. ISBN 978-0-201-36121-6. OCLC  837386973 .
  9. ^ Hopcroft, John ; Tarjan, Robert E. (1974), "Efficient planarity testing" (PDF) , Journal of the Association for Computing Machinery , 21 (4): 549–568, doi : 10.1145 / 321850.321852 , hdl : 1813/6011.
  10. ^ de Fraysseix, H .; Ossona de Méndez, P .; Rosenstiehl, P. (2006), "Trémaux Trees and Planarity", International Journal of Foundations of Computer Science , 17 (5): 1017–1030, arXiv : math / 0610935 , doi : 10.1142 / S0129054106004248.
  11. ^ Reif, John H. (1985). "La búsqueda en profundidad es inherentemente secuencial". Cartas de procesamiento de información . 20 (5). doi : 10.1016 / 0020-0190 (85) 90024-9 .
  12. ^ Mehlhorn, Kurt ; Sanders, Peter (2008). Algoritmos y estructuras de datos: la caja de herramientas básica (PDF) . Saltador.
  13. ^ Aggarwal, A .; Anderson, RJ (1988), "Un algoritmo NC aleatorio para la primera búsqueda en profundidad", Combinatorica , 8 (1): 1–12, doi : 10.1007 / BF02122548 , MR  0951989.
  14. ^ Karger, David R .; Motwani, Rajeev (1997), "Un algoritmo NC para cortes mínimos", SIAM Journal on Computing , 26 (1): 255–272, CiteSeerX  10.1.1.33.1701 , doi : 10.1137 / S0097539794273083 , MR  1431256.

  • Thomas H. Cormen , Charles E. Leiserson , Ronald L. Rivest y Clifford Stein . Introducción a los algoritmos , segunda edición. MIT Press y McGraw-Hill, 2001. ISBN  0-262-03293-7 . Sección 22.3: Búsqueda en profundidad, págs. 540–549.
  • Goodrich, Michael T .; Tamassia, Roberto (2001), Diseño de algoritmos: fundamentos, análisis y ejemplos de Internet , Wiley, ISBN 0-471-38365-1
  • Kleinberg, Jon ; Tardos, Éva (2006), Algorithm Design , Addison Wesley, págs. 92–94
  • Knuth, Donald E. (1997), The Art of Computer Programming Vol 1. 3ª ed , Boston: Addison-Wesley, ISBN 0-201-89683-4, OCLC  155842391

  • Estructuras de datos abiertas - Sección 12.3.2 - Profundidad: primera búsqueda , Pat Morin
  • Biblioteca de gráficos de C ++ Boost: búsqueda en profundidad
  • Animación de búsqueda en profundidad (para un gráfico dirigido)
  • Búsqueda en profundidad y en amplitud: explicación y código
  • QuickGraph , primer ejemplo de búsqueda en profundidad para .Net
  • Explicación ilustrada del algoritmo de búsqueda en profundidad (implementaciones de Java y C ++)
  • YAGSBPL: una biblioteca de C ++ basada en plantillas para la búsqueda y planificación de gráficos