Articulo de referencia

Búsqueda en profundidad

O(|V| + |E|) for explicit graphs traversed without repetition, O(b^d) for implicit graphs with branching factor ''b '' searched to depth ''d''"},"space":{"wt":" O(|V|) if entire...

En informática , la búsqueda en profundidad ( DFS ) es un algoritmo para recorrer o buscar en estructuras de datos de árboles o grafos . El algoritmo comienza en el nodo raíz (seleccionando un nodo arbitrario como raíz en el caso de un grafo) y explora lo más lejos posible a lo largo de cada rama antes de retroceder. Se necesita memoria adicional, generalmente una pila , para mantener un registro de los nodos descubiertos hasta el momento a lo largo de una rama específica, lo que facilita el retroceso en el grafo.

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 una estrategia para resolver laberintos . [ 2 ] [ 3 ]

Propiedades

El análisis de tiempo y espacio de DFS difiere según su área de aplicación. En la informática teórica, DFS se utiliza normalmente para recorrer un grafo completo y requiere tiempo.O(|V|+|mi|){\displaystyle O(|V|+|E|)}, [ 4 ] donde|V|{\displaystyle |V|}es el número de vértices y|mi|{\displaystyle |E|}el número de aristas . Esto es lineal en el tamaño del grafo. En estas aplicaciones también utiliza espacio.O(|V|){\displaystyle O(|V|)}En el peor de los casos, se almacena 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 este contexto, los límites de tiempo y espacio son los mismos que para la búsqueda en amplitud, y la elección de cuál de estos dos algoritmos utilizar depende menos de su complejidad y más de las diferentes propiedades de los ordenamientos de vértices que producen.

Para aplicaciones de DFS en relación con dominios específicos, como la búsqueda de soluciones en inteligencia artificial o el rastreo web, el grafo a recorrer suele ser demasiado grande para visitarlo por completo o infinito (DFS puede sufrir de no terminación ). En tales casos, la búsqueda se realiza solo hasta una profundidad limitada ; debido a los recursos limitados, como la memoria o el espacio en disco, normalmente no se utilizan estructuras de datos para mantener un registro del conjunto de todos los vértices visitados previamente. Cuando la búsqueda se realiza hasta 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 igual al tamaño del grafo completo, ya que algunos vértices pueden buscarse más de una vez y otros ninguna), pero la complejidad espacial de esta variante de DFS es solo proporcional al límite de profundidad y, como resultado, es mucho menor que el espacio necesario para buscar a la misma profundidad utilizando la búsqueda en anchura. Para tales aplicaciones, DFS también se presta mucho mejor a los métodos heurísticos para elegir una rama que parezca probable. Cuando no se conoce de antemano un límite de profundidad adecuado, la búsqueda en profundidad iterativa aplica repetidamente la DFS 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 búsqueda en profundidad iterativa aumenta el tiempo de ejecución solo en un factor constante con respecto al caso en el que se conoce el límite de profundidad correcto, debido al crecimiento geométrico del número de nodos por nivel.

El DFS también puede utilizarse para recopilar una muestra de nodos de un grafo. Sin embargo, el DFS incompleto, al igual que el BFS incompleto , está sesgado hacia nodos de alto grado .

Ejemplo

Ejemplo animado de una búsqueda en profundidad

Para el siguiente gráfico:

Un grafo no dirigido con aristas AB, BD, BF, FE, AC, CG, AE.

Una búsqueda en profundidad que comience en el nodo A, suponiendo que las aristas izquierdas del grafo mostrado se eligen antes que las derechas, y suponiendo que la búsqueda recuerde los nodos visitados previamente y no los repita (ya que se trata de un grafo pequeño), visitará los nodos en el siguiente orden: A, B, D, F, E, C, G. Las aristas recorridas 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 previamente da como resultado visitar los nodos en el orden A, B, D, F, E, A, B, D, F, E, etc., indefinidamente, quedando atrapado en el ciclo A, B, D, F, E y sin llegar nunca a C o G.

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

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

El resultado de una búsqueda en profundidad de un grafo se puede describir convenientemente mediante un árbol de expansión formado por los vértices alcanzados durante la búsqueda. A partir de este árbol de expansión, las aristas del grafo original se pueden clasificar en tres tipos: aristas directas , que apuntan desde un nodo del árbol a uno de sus descendientes; aristas inversas , que apuntan desde un nodo a uno de sus ancestros; y aristas cruzadas , que no realizan ninguna de las dos funciones. En ocasiones, las aristas del árbol , que pertenecen al propio árbol de expansión, se clasifican por separado de las aristas directas. Si el grafo original no está dirigido, todas sus aristas son del árbol o inversas.

Órdenes de vértices

También es posible utilizar la búsqueda en profundidad para ordenar linealmente los vértices de un grafo o árbol. Hay cuatro maneras posibles de hacerlo:

  • Un preordenamiento 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 preordenamiento de un árbol de expresiones es la expresión en notación polaca .
  • Un postordenamiento es una lista de los vértices en el orden en que fueron visitados por última vez por el algoritmo. Un postordenamiento de un árbol de expresiones es la expresión en notación polaca inversa .
  • Un preordenamiento inverso es lo contrario de un preordenamiento, es decir, una lista de los vértices en el orden opuesto al de su primera visita. El preordenamiento inverso no es lo mismo que el postordenamiento.
  • Un postordenamiento inverso es lo contrario de un postordenamiento, es decir, una lista de los vértices en el orden opuesto al de su última visita. El postordenamiento inverso no es lo mismo que el preordenamiento.

Para los árboles binarios existe además el ordenamiento y el ordenamiento inverso .

Por ejemplo, al recorrer el grafo dirigido que se muestra a continuación, comenzando en el nodo A, la secuencia de recorridos es ABDBACA o ACDCABA (la decisión de visitar primero B o C desde A depende del algoritmo). Cabe destacar que las visitas repetidas, en forma de retroceso a un nodo para comprobar si aún tiene vecinos sin visitar, se incluyen aquí (incluso si no tiene ninguno). Por lo tanto, los posibles preordenamientos son ABDC y ACDB, mientras que los posibles postordenamientos son DBCA y DCBA, y los posibles postordenamientos inversos son ACBD y ABC D.

Un grafo dirigido con aristas AB, BD, AC, CD

El postordenamiento inverso produce una ordenación topológica de cualquier grafo dirigido acíclico . Este ordenamiento también resulta útil en el análisis del flujo de control, ya que suele representar una linealización natural de dichos flujos. El grafo anterior podría representar el flujo de control del fragmento de código que se muestra a continuación, y es natural considerar este código en el orden ABCD o ACBD, pero no en el orden ABDC o ACD B.

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

Pseudocódigo

0

1
Nodo anterior Reiniciar Inicio
Demostración interactiva de búsqueda en profundidad

Una implementación recursiva de DFS: [ 5 ]

El procedimiento DFS( G , v ) etiqueta v como descubierto para todos los bordes dirigidos de v a w que están en G .adjacentEdges( v ) hacer si el vértice w no está etiquetado como descubierto entonces llamar recursivamente a DFS( G , w )

Una implementación no recursiva de DFS con complejidad espacial en el peor de los casos.O(|mi|){\displaystyle O(|E|)}, con la posibilidad de vértices duplicados en la pila: [ 6 ]

El procedimiento DFS_iterative( G , v ) es: sea S una pila S .push( v ) mientras S no esté vacía hacer v = S .pop() si v no está etiquetado como descubierto entonces etiquetar v como descubierto para todos los bordes de v a w en G .adjacentEdges( v ) hacer S .push( w )
Un grafo no dirigido con aristas AB, BD, BF, FE, AC, CG, AE.
El gráfico de ejemplo, copiado de arriba

Estas dos variantes de DFS visitan los vecinos de cada vértice en orden inverso: el primer vecino de v visitado por la variante recursiva es el primero en la lista de aristas adyacentes, mientras que en la variante iterativa el primer vecino visitado es el último en la lista de aristas adyacentes. La implementación recursiva visitará los nodos del grafo de ejemplo en el siguiente orden: A, B, D, F, E, C, G. La implementación no recursiva visitará los nodos en el siguiente orden: A, E, F, B, D, C, G.

La implementación no recursiva es similar a la búsqueda en anchura, pero difiere de ella en dos aspectos:

  1. utiliza una pila en lugar de una cola, y
  2. Esto 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 añadir el vértice.

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

Otra posible implementación de la búsqueda en profundidad iterativa 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 la búsqueda en profundidad recursiva. [ 8 ]

El procedimiento DFS_iterative( G , v ) es sea S una pila etiqueta v como descubierto S .push(iterador de G .adjacentEdges( v )) mientras S no esté vacío hacer si S .peek().hasNext() entonces w = S .peek().next() si w no está etiquetado como descubierto entonces etiqueta w como descubierto S .push(iterador de G .adjacentEdges( w )) sino S .pop()

Aplicaciones

Algoritmo aleatorio similar a la búsqueda en profundidad utilizado para generar un laberinto.

Los algoritmos que utilizan la búsqueda en profundidad como elemento básico incluyen:

Complejidad

La complejidad computacional de DFS fue investigada por John Reif . Más precisamente, dado un grafoGRAMO{\displaystyle G}, dejarO=(v1,,vnorte){\displaystyle O=(v_{1},\dots,v_{n})}sea ​​el ordenamiento calculado por el algoritmo DFS recursivo estándar. Este ordenamiento se denomina ordenamiento de búsqueda en profundidad lexicográfica. John Reif consideró la complejidad de calcular el ordenamiento de búsqueda en profundidad lexicográfica, dado un grafo y una fuente. Una versión de decisión del problema (probar si algún vértice u aparece antes que algún vértice v en este orden) es P -completa , [ 12 ] lo que significa que es "una pesadilla para el procesamiento paralelo ". [ 13 ] : 189

Un ordenamiento de búsqueda en profundidad (no necesariamente el lexicográfico) puede ser calculado por un algoritmo paralelo aleatorio en la clase de complejidad RNC . [ 14 ] Hasta 1997, seguía siendo desconocido si un recorrido en profundidad podía ser construido por un algoritmo paralelo determinista, en la clase de complejidad NC . [ 15 ]

Véase también

Notas

  1. Charles Pierre Trémaux (1859–1882) École polytechnique de París (X:1876), ingeniero francés del telégrafoen Conferencia pública, 2 de diciembre de 2010 – por el profesor Jean Pelletier-Thibert en la Académie de Macon (Borgoña – Francia) – (Resumen publicado en los Annals academic, marzo de 2011 – ISSN 0980-6032 ) 
  2. Even, Shimon (2011), Algoritmos de grafos (2.ª ed.), Cambridge University Press, págs. 46–48 , ISBN   978-0-521-73653-4.
  3. Sedgewick, Robert (2002), Algoritmos en C++: Algoritmos de grafos (3.ª 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 grafos basado en pilas ≠ 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-6OCLC 837386973 
  9. Hopcroft, John ; Tarjan, Robert E. (1974), "Pruebas de planaridad eficientes" (PDF) , Journal of the Association for Computing Machinery , 21 (4): 549–568 , doi : 10.1145/321850.321852 , hdl : 1813/6011 , S2CID 6279825 .
  10. de Fraysseix, H.; Ossona de Mendez, P. ; Rosenstiehl, P. (2006), "Árboles de trémaux y planaridad", International Journal of Foundations of Computer Science , 17 (5): 1017– 1030, arXiv : math/0610935 , Bibcode : 2006math.....10935D , doi : 10.1142/S0129054106004248 , S2CID 40107560 .
  11. Baccelli, Francois; Haji-Mirsadeghi, Mir-Omid; Khezeli, Ali (2018), "Árboles genealógicos eternos y dinámica en grafos aleatorios unimodulares", en Sobieczky, Florian (ed.), Unimodularidad en grafos generados aleatoriamente: Sesión especial de la AMS, 8-9 de octubre de 2016, Denver, Colorado , Contemporary Mathematics, vol. 719, Providence, Rhode Island: American Mathematical Society, pp. 85-127 , arXiv : 1608.05940 , doi : 10.1090/conm/719/14471 , ISBN   978-1-4704-3914-9, MR 3880014 , S2CID 119173820  ; véase el Ejemplo 3.7, pág. 93
  12. Reif, John H. (1985). "La búsqueda en profundidad es inherentemente secuencial". Information Processing Letters . 20 (5): 229– 234. doi : 10.1016/0020-0190(85)90024-9 .
  13. Mehlhorn, Kurt ; Sanders, Peter (2008). Algoritmos y estructuras de datos: la caja de herramientas básica (PDF) . Springer. Archivado (PDF) del original el 8 de septiembre de 2015.
  14. Aggarwal, A.; Anderson, RJ (1988), "Un algoritmo NC aleatorio para búsqueda en profundidad", Combinatorica , 8 (1): 1– 12, doi : 10.1007/BF02122548 , MR 0951989 , S2CID 29440871  .
  15. ^ 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  .

Referencias

  • Estructuras de datos abiertas - Sección 12.3.2 - Búsqueda en profundidad , Pat Morin
  • Biblioteca C++ Boost Graph: Búsqueda en profundidad
  • Animación de búsqueda en profundidad (para un grafo dirigido)
  • Búsqueda en profundidad y búsqueda en amplitud: explicación y código.
  • Explicación ilustrada del algoritmo de búsqueda en profundidad (implementaciones en Java y C++)
  • YAGSBPL: una biblioteca C++ basada en plantillas para la búsqueda y planificación de grafos.
  • Visualización de la búsqueda en profundidad