Una cerca en profunditat (en anglès Depth First Search, DFS) és un algorisme que permet recórrer tots els nodes d'un arbre o graf de manera ordenada, però no uniforme. El seu funcionament es basa a anar expandint cada node que troba, de manera recursiva, recorrent tots els nodes d'un camí concret. Quan ja no queden més nodes per visitar d'aquest camí, es realitza un pas enrere (Backtracking), que permet que pugui tornar a començar el mateix procés amb cadascun dels germans d'un node ja processat.
De la mateixa manera, existeix l'algorisme de cerca en amplada (BFS - breadth first search).
El segle xix, el matemàtic francès Charles Pierre Trémaux investigà una versió de l'algorisme de cerca en profunditat com a estratègia per a la resolució de laberints.[1][2]
L'anàlisi del temps i l'espai necessaris per a l'algorisme DFS difereix en funció de l'àrea d'aplicació. En informàtica teòrica, l'algorisme DFS s'utilitza per recórrer un graf sencer, i necessita un temps Θ(|V| + |E|),[3] lineal en la grandària del graf. En aquestes aplicacions, també necessita espai O(|V|) en el cas pitjor per emmagatzemar la pila de vèrtexs del camí de cerca en curs, així com el conjunt de vèrtexs ja visitats. Per tant, les fites de temps i espai són les mateixes que per l'algorisme de cerca en amplada, i l'elecció de quin d'aquests dos algorismes cal utilitzar no depèn tant de la seva complexitat com de les diferents propietats de les ordenacions de vèrtexs que produeixen.
Quant a les aplicacions de l'algorisme DFS en àrees específiques, com la cerca de solucions en intel·ligència artificial o el recorregut de pàgines web per part d'aranyes web, el graf que cal recórrer és o bé molt gran o bé infinit (DFS pot patir parades). En aquests casos, una solució pot ser limitar la cerca només fins a una certa profunditat; a causa de la limitació de recursos, com la memòria o l'espai en disc, no s'acostumen a utilitzar estructures de dades per guardar l'historial de quins vèrtexs s'han visitat. Quan es realitza la cerca fins a una profunditat limitada, el temps encara és lineal en termes del nombre de vèrtexs expandits i arestes (encara que aquest nombre no és el mateix que la grandària de tot el graf, ja que pot ser que alguns vèrtexs es recorrin més d'un cop i d'altres no es visitin), però la complexitat en espai d'aquesta variant de DFS només és proporcional a la profunditat límit, i com a resultat, molt menor que l'espai necessari per explorar fins a la mateixa profunditat mitjançant la cerca en amplada. Per a aquestes aplicacions, DFS resulta millor en la implementació de mètodes heurístics per escollir una branca apropiada. Quan no es coneix a priori una profunditat límit adequada, la cerca en profunditat iterativa aplica l'algorisme DFS repetidament amb una successió de límits creixent per a la profunditat. En el mode d'anàlisi de la intel·ligència artificial, amb un factor de ramificació superior a 1, la cerca en profunditat iterativa incrementa el temps de procés només en un factor constant sobre el cas en què es conegui prèviament la profunditat correcta, a causa del creixement geomètric del nombre de nodes per nivell.
L'algorisme de cerca en profunditat també es pot fer servir per recollir una mostra dels nodes del graf. Tanmateix, l'algorisme DFS incomplet, de manera semblant a BFS incomplet, és esbiaixat cap als nodes de grau elevat.
Un exemple d'arbre és:
El resultat d'aplicar l'algorisme de cerca en profunditat sobre ell, començant per F, seria el recorregut següent: F, B, A, D, C, E, G, I, H.
Per al graf següent:
una cerca en profunditat que comenci en A, suposant que les arestes esquerres es recorren abans que les dretes, i suposant que la cerca recorda els vèrtexs visitats i no els repeteix (posat que aquest és un graf petit), recorre els nodes en aquest ordre: A, B, D, F, E, C, G. Les arestes recorregudes formen un arbre de Trémaux, una estructura amb aplicacions importants en teoria de grafs.
Si es realitza la mateixa cerca sense recordar els vèrtexs prèviament visitats, l'ordre seria A, B, D, F, E, A, B, D, F, E, etc. infinitament, atrapada en el cicle A, B, D, F, E i mai no arribaria a C o G.
La cerca en profunditat iterativa és una tècnica per evitar aquest bucle infinit i s'arribaria a tots els nodes.
Una descripció convenient d'una cerca en profunditat d'un graf és en termes d'un arbre d'expansió dels vèrtexs visitats durant la cerca. Basant-se en aquest arbre d'expansió, es poden classificar les arestes del graf original en tres tipus: arestes cap endavant, que apunten d'un node de l'arbre cap a un dels seus descendents, arestes cap enrere, que apunten d'un node a un dels seus superiors, i arestes creuades, que són aquelles que no pertanyen a cap dels dos tipus anteriors. De vegades, hom afegeix un quart tipus separat de les arestes cap endavant, les arestes de l'arbre, que són les arestes que pertanyen a l'arbre d'expansió. Si el graf original és no dirigit, llavors totes les seves arestes són arestes de l'arbre o arestes cap enrere.
També és possible emprar la cerca en profunditat per ordenar linealment els vèrtexs del graf (o arbre) original. Hi ha tres maneres habituals de fer-ho:
si (A) llavors { B } sinó { C } D
Entrada: Un graf G i un vèrtex v de G.
Sortida: Tots els vèrtexs que es poden visitar des de v, marcats com a visitats.
Una implementació recursiva de DFS:[4][5]
1 procediment DFS(G,v): 2 etiquetar v com a visitat 3 per a tota aresta de v cap a w en G.arestesAdjacents(v) fer 4 si el vèrtex w no està etiquetat com a visitat llavors 5 cridar recursivament DFS(G,w)
Una implementació no recursiva de DFS:[6]
1 procediment DFS-iteratiu(G,v): 2 sigui S una pila 3 S.empila(v) 4 mentre S no és buida 5 v = S.desempila() 6 si v no està etiquetat com a visitat: 7 etiquetar v com a visitat 8 per a tota aresta de v cap a w en G.arestesAdjacents(v) fer 9 S.empila(w)
Aquestes dues variants de l'algorisme DFS visiten els veïns de cada vèrtex en ordre invers l'una de l'altra: el primer veí de v que visita la variant recursiva és el primer en la llista d'arestes adjacents, mentre que en la variant iterativa el primer vèrtex visitat és l'últim de la llista d'arestes adjacents. La implementació recursiva visita els nods del graf d'exemple en l'ordre A, B, D, F, E, C, G. La implementació iterativa visita els nodes en l'ordre A, E, F, B, D, C, G. La variant iterativa és semblant a la cerca en amplada, però difereixen en dos aspectes: utilitza una pila en comptes d'una cua, i no es comprova si un vèrtex s'ha visitat fins que aquest vèrtex s'ha desempilat, en comptes de verificar-ho abans d'empilar-lo. Notem que aquesta implementació iterativa de DFS pot arribar a utilitzar un espai O(|E|) en el cas pitjor, per exemple en un graf complet.
Alguns problemes que admeten solucions mitjançant l'aplicació de la cerca en profunditat són:
La complexitat computacional de l'algorisme DFS fou investigada per Reif, que va demostrar que una versió de decisió (determinar si un vèrtex u apareix abans d'un vèrtex v en una ordenació DFS d'un graf amb arrel) és P-complet,[9] la qual cosa significava que és "un malson per al processament paral·lel".[10]
La complexitat en temps és per a grafs explícits recorreguts sense repetició, i per a grafs implícits amb un factor de ramificació b explorats fins a la profunditat d. La complexitat en espai és si s'explora el graf sencer sense repetició, i O(longitud del camí més llarg recorregut) per a grafs implícits sense eliminació dels nodes duplicats.