Primeira pesquisa em profundidade em Java
1. Visão geral
Neste tutorial, exploraremos a pesquisa em profundidade em Java.
A pesquisa por profundidade (DFS) é um algoritmo transversal usado para estruturas de dados em árvore e em gráfico. A profundidade search goes deep in each branch before moving to explore another branch primeiro.
Nas próximas seções, primeiro daremos uma olhada na implementação de uma Árvore e, em seguida, de um Gráfico.
Para ver como implementar essas estruturas em Java, dê uma olhada em nossos tutoriais anteriores emBinary TreeeGraph.
2. Pesquisa em profundidade de árvore
Há três pedidos diferentes para percorrer uma árvore usando o DFS:
-
Pré-encomenda Traversal
-
Travessia Inorder
-
Traversal de pós-encomenda
2.1. Pré-encomenda Traversal
Na travessia da pré-ordem, percorremos a raiz primeiro, depois as subárvores esquerda e direita.
Podemos simplesmenteimplement preorder traversal using recursion:
-
Visite o nócurrent
-
Percorrer a subárvoreleft
-
Percorrer a subárvoreright
public void traversePreOrder(Node node) {
if (node != null) {
visit(node.value);
traversePreOrder(node.left);
traversePreOrder(node.right);
}
}
Também podemos implementar o percurso de pré-encomenda sem recursão.
To implement an iterative preorder traversal, we’ll need a Stack, e seguiremos estas etapas:
-
Pressioneroot em nosso stack
-
Enquantostack não está vazio
-
Nó Popcurrent
-
Visite o nócurrent
-
Empurre o filhoright, depois o filholeft parastack
-
public void traversePreOrderWithoutRecursion() {
Stack stack = new Stack();
Node current = root;
stack.push(root);
while(!stack.isEmpty()) {
current = stack.pop();
visit(current.value);
if(current.right != null) {
stack.push(current.right);
}
if(current.left != null) {
stack.push(current.left);
}
}
}
2.2. Travessia Inorder
Para travessia em ordem,we traverse the left subtree first, then the root, then finally the right subtree.
A travessia na ordem para uma árvore de pesquisa binária significa atravessar os nós na ordem crescente de seus valores.
Podemos simplesmente implementar a travessia inorder usando recursão:
public void traverseInOrder(Node node) {
if (node != null) {
traverseInOrder(node.left);
visit(node.value);
traverseInOrder(node.right);
}
}
Também podemosimplement inorder traversal without recursion:
-
Empurre o nóroot parastack
-
Enquanto stack não está vazio
-
Continue empurrandoleft filho parastack, até chegarmos ao filho mais à esquerda do nócurrent
-
Visite o nócurrent
-
Empurreright filho parastack
-
public void traverseInOrderWithoutRecursion() {
Stack stack = new Stack();
Node current = root;
stack.push(root);
while(! stack.isEmpty()) {
while(current.left != null) {
current = current.left;
stack.push(current);
}
current = stack.pop();
visit(current.value);
if(current.right != null) {
current = current.right;
stack.push(current);
}
}
}
2.3. Traversal de pós-encomenda
Finalmente, na travessia pós-ordem,we traverse the left and right subtree before we traverse the root.
Podemos seguir nossorecursive solution anterior:
public void traversePostOrder(Node node) {
if (node != null) {
traversePostOrder(node.left);
traversePostOrder(node.right);
visit(node.value);
}
}
Ou, também podemosimplement postorder traversal without recursion:
-
Empurre o nóroot emstack
-
Enquanto stack não está vazio
-
Verifique se já atravessamos a subárvore esquerda e direita
-
Caso contrário, empurreright filho eleft filho emstack
-
public void traversePostOrderWithoutRecursion() {
Stack stack = new Stack();
Node prev = root;
Node current = root;
stack.push(root);
while (!stack.isEmpty()) {
current = stack.peek();
boolean hasChild = (current.left != null || current.right != null);
boolean isPrevLastChild = (prev == current.right ||
(prev == current.left && current.right == null));
if (!hasChild || isPrevLastChild) {
current = stack.pop();
visit(current.value);
prev = current;
} else {
if (current.right != null) {
stack.push(current.right);
}
if (current.left != null) {
stack.push(current.left);
}
}
}
}
3. Pesquisa em profundidade do gráfico
A principal diferença entre gráficos e árvores é quegraphs may contain cycles.
Portanto, para evitar a pesquisa em ciclos, marcaremos cada nó quando o visitarmos.
Veremos duas implementações para gráfico DFS, com recursão e sem recursão.
3.1. Gráfico DFS com recursão
Primeiro, vamos começar simples com recursão:
-
Vamos começar de um determinado nó
-
Marcar o nócurrent como visitado
-
Visite o nócurrent
-
Atravessar vértices adjacentes não visitados
public void dfs(int start) {
boolean[] isVisited = new boolean[adjVertices.size()];
dfsRecursive(start, isVisited);
}
private void dfsRecursive(int current, boolean[] isVisited) {
isVisited[current] = true;
visit(current);
for (int dest : adjVertices.get(current)) {
if (!isVisited[dest])
dfsRecursive(dest, isVisited);
}
}
3.2. Gráfico DFS sem recursão
Também podemos implementar o DFS do gráfico sem recursão. Vamos simplesmente usar umStack:
-
Vamos começar de um determinado nó
-
Empurre o nóstart parastack
-
EnquantoStack não está vazio
-
Marcar o nócurrent como visitado
-
Visite o nócurrent
-
Empurre vértices adjacentes não visitados
-
public void dfsWithoutRecursion(int start) {
Stack stack = new Stack();
boolean[] isVisited = new boolean[adjVertices.size()];
stack.push(start);
while (!stack.isEmpty()) {
int current = stack.pop();
isVisited[current] = true;
visit(current);
for (int dest : adjVertices.get(current)) {
if (!isVisited[dest])
stack.push(dest);
}
}
}
3.4. Classificação topológica
Existem muitos aplicativos para a pesquisa em profundidade do gráfico. Uma das aplicações famosas do DFS é a Classificação Topológica.
A classificação topológica para um grafo direcionado é uma ordenação linear de seus vértices de forma que para cada aresta o nó de origem venha antes do destino.
Para obter a classificação topológica, precisaremos de uma adição simples ao DFS que acabamos de implementar:
-
Precisamos manter os vértices visitados em uma pilha, porque a classificação topológica são os vértices visitados em uma ordem inversa
-
Empurramos o nó visitado para a pilha somente depois de atravessar todos os seus vizinhos
public List topologicalSort(int start) {
LinkedList result = new LinkedList();
boolean[] isVisited = new boolean[adjVertices.size()];
topologicalSortRecursive(start, isVisited, result);
return result;
}
private void topologicalSortRecursive(int current, boolean[] isVisited, LinkedList result) {
isVisited[current] = true;
for (int dest : adjVertices.get(current)) {
if (!isVisited[dest])
topologicalSortRecursive(dest, isVisited, result);
}
result.addFirst(current);
}
4. Conclusão
Neste artigo, discutimos a pesquisa profunda para as estruturas de dados Árvore e Gráfico.
O código-fonte completo está disponível emGitHub.