1. Обзор
В этом уроке мы рассмотрим углубленный поиск на Java.
Поиск по глубине (DFS) -это алгоритм обхода, используемый как для древовидной, так и для графовой структур данных|/. Поиск по глубине сначала углубляется в каждую ветвь, прежде чем перейти к изучению другой ветви .
В следующих разделах мы сначала рассмотрим реализацию дерева, а затем Графика.
Чтобы узнать, как реализовать эти структуры в Java, ознакомьтесь с нашими предыдущими учебными пособиями по двоичному дереву и графику .
2. Глубина дерева-первый поиск
Существует три различных порядка обхода дерева с помощью DFS:
- Обход предварительного заказа
- Обход По Порядку
- Обход после заказа
2.1. Обход Предварительного Заказа
При обходе по предварительному заказу мы сначала проходим корень, затем левое и правое поддеревья.
Мы можем просто реализовать обход предварительного заказа с помощью рекурсии :
- Посетите текущий узел
- Пересечение влево поддерево
- Пересечение вправо поддерево
public void traversePreOrder(Node node) { if (node != null) { visit(node.value); traversePreOrder(node.left); traversePreOrder(node.right); } }
Мы также можем реализовать обход предварительного заказа без рекурсии.
Для реализации итеративного обхода предварительного заказа нам понадобится Стек , и мы выполним следующие действия:
- Нажмите root в нашем s tack
Пока стек не пуст
- Pop текущий узел
- Посетите текущий узел
- Нажмите вправо дочерний элемент, затем влево дочерний элемент в стек
public void traversePreOrderWithoutRecursion() { Stackstack = 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. Обход По Порядку
Для обхода по порядку мы сначала проходим левое поддерево, затем корневое, затем, наконец, правое поддерево .
Обход по порядку для двоичного дерева поиска означает обход узлов в порядке возрастания их значений.
Мы можем просто реализовать обход по порядку, используя рекурсию:
public void traverseInOrder(Node node) { if (node != null) { traverseInOrder(node.left); visit(node.value); traverseInOrder(node.right); } }
Мы также можем реализовать обход по порядку без рекурсии , тоже:
- Нажмите корневой узел на s галс
В то время как s галс не пуст
- Продолжайте нажимать влево дочерний элемент в стек, пока мы не достигнем текущего самого левого дочернего узла
- Посетите текущий узел
- Нажмите вправо дочерний элемент на стек
public void traverseInOrderWithoutRecursion() { Stackstack = 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. Обход После Заказа
Наконец, при обходе после заказа мы проходим левое и правое поддерево, прежде чем перейдем к корню .
Мы можем следовать нашему предыдущему рекурсивному решению :
public void traversePostOrder(Node node) { if (node != null) { traversePostOrder(node.left); traversePostOrder(node.right); visit(node.value); } }
Или мы также можем реализовать обход после заказа без рекурсии :
- Нажмите корневой узел в s галс
В то время как s галс не пуст
- Проверьте, прошли ли мы уже левое и правое поддерево
- Если нет, то нажмите вправо дочерний элемент и влево дочерний элемент в стек
public void traversePostOrderWithoutRecursion() { Stackstack = 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. Глубина графика-первый поиск
Основное различие между графами и деревьями заключается в том, что графики могут содержать циклы .
Поэтому, чтобы избежать циклического поиска, мы будем отмечать каждый узел при его посещении.
Мы увидим две реализации для графовых DFS, с рекурсией и без рекурсии.
3.1. График DFS с рекурсией
Во-первых, давайте начнем просто с рекурсии:
- Мы начнем с заданного узла
- Отметьте текущий узел как посещенный
- Посетите текущий узел
- Пересеките не посещенные соседние вершины
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. График DFS без рекурсии
Мы также можем реализовать графовые DFS без рекурсии. Мы просто будем использовать Стек :
- Мы начнем с заданного узла
- Вставьте запуск узел в стек
В то время как Стек не пуст
- Отметьте текущий узел как посещенный
- Посетите текущий узел
- Нажмите на не посещенные соседние вершины
public void dfsWithoutRecursion(int start) { Stackstack = 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. Топологическая Сортировка
Существует множество приложений для поиска по глубине графика в первую очередь. Одним из известных приложений для DFS является топологическая сортировка.
Топологическая сортировка для ориентированного графа – это линейное упорядочение его вершин таким образом, чтобы для каждого ребра исходный узел находился перед конечным.
Чтобы получить топологическую сортировку, нам понадобится простое дополнение к DFS, которое мы только что реализовали:
- Нам нужно сохранить посещенные вершины в стеке, потому что топологическая сортировка-это посещенные вершины в обратном порядке
- Мы помещаем посещенный узел в стек только после обхода всех его соседей
public ListtopologicalSort(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. Заключение
В этой статье мы обсудили углубленный поиск как для древовидной, так и для графовой структур данных.
Полный исходный код доступен на GitHub .