栈和深度优先搜索(DFS)
與 BFS 類似,深度優先搜索(DFS)是用于在樹/圖中遍歷/搜索的另一種重要算法。也可以在更抽象的場景中使用。
正如樹的遍歷中所提到的,我們可以用 DFS 進行 前序遍歷,中序遍歷 和 后序遍歷。在這三個遍歷順序中有一個共同的特性:除非我們到達最深的結點,否則我們永遠不會回溯。
這也是 DFS 和 BFS 之間最大的區別,BFS永遠不會深入探索,除非它已經在當前層級訪問了所有結點。
模版
遞歸模版
有兩種實現 DFS 的方法。第一種方法是進行遞歸:
boolean DFS(Node cur, Node target, Set<Node> visited) {return true if cur is target;for (next : each neighbor of cur) {if (next is not in visited) {add next to visted;return true if DFS(next, target, visited) == true;}}return false; }當我們遞歸地實現 DFS 時,似乎不需要使用任何棧。但實際上,我們使用的是由系統提供的隱式棧,也稱為調用棧(Call Stack)。
顯式棧模板
遞歸解決方案的優點是它更容易實現。 但是,存在一個很大的缺點:如果遞歸的深度太高,你將遭受堆棧溢出。 在這種情況下,您可能會希望使用 BFS,或使用 顯式棧 實現 DFS。
boolean DFS(int root, int target) {Set<Node> visited;Stack<Node> s;add root to s;while (s is not empty) {Node cur = the top element in s;return true if cur is target;for (Node next : the neighbors of cur) {if (next is not in visited) {add next to s;add next to visited;}}remove cur from s;}return false; }例題
1.島嶼數量
- 難度:Medium
題目描述
給定一個由 '1'(陸地)和 '0'(水)組成的的二維網格,計算島嶼的數量。一個島被水包圍,并且它是通過水平方向或垂直方向上相鄰的陸地連接而成的。你可以假設網格的四個邊均被水包圍。
示例 1:
輸入:
11110
11010
11000
00000
輸出: 1
示例 2:
輸入:
11000
11000
00100
00011
輸出: 3
解題思路及實現
筆者曾經在 這篇文章 中展示了如何使用 BFS 解決這道題,事實上該題使用 DFS 更簡單,因為前者還需要一個隊列維護 廣度優先搜索 過程中搜索的層級信息。
使用 DFS 解題如下:
public class B200NumIslands {public int numIslands(char[][] grid) {int nr = grid.length;if (nr == 0) return 0;int nc = grid[0].length;if (nc == 0) return 0;int result = 0;for (int r = 0; r < nr; r++) {for (int c = 0; c < nc; c++) {if (grid[r][c] == '1') {result++;dfs(grid, r, c);}}}return result;}private void dfs(char[][] grid, int r, int c) {int nr = grid.length;int nc = grid[0].length;// 排除邊界外的情況if (r >= nr || c >= nc || r < 0 || c < 0) return;// 排除邊界外指定位置為 '0' 的情況if (grid[r][c] == '0') return;// 該位置為一個島,標記為已探索grid[r][c] = '0';dfs(grid, r - 1, c); // topdfs(grid, r + 1, c); // bottomdfs(grid, r, c - 1); // leftdfs(grid, r, c + 1); // right} }2.克隆圖
- 難度:Medium
題目描述
給你無向 連通 圖中一個節點的引用,請你返回該圖的 深拷貝(克隆)。
圖中的每個節點都包含它的值 val(int) 和其鄰居的列表(list[Node])。
class Node {public int val;public List<Node> neighbors; }更詳細的題目描述參考 這里 :
https://leetcode-cn.com/problems/clone-graph/
解題思路及實現
題目比較難理解,需要注意的是:
用 DFS 實現代碼如下:
class Solution {public Node cloneGraph(Node node) {HashMap<Node,Node> map = new HashMap<>();return dfs(node, map);}private Node dfs(Node root, HashMap<Node,Node> map) {if (root == null) return null;if (map.containsKey(root)) return map.get(root);Node clone = new Node(root.val, new ArrayList());map.put(root, clone);for (Node nei: root.neighbors) {clone.neighbors.add(dfs(nei, map));}return clone;} }3.目標和
- 難度:Medium
題目描述
給定一個非負整數數組,a1, a2, …, an, 和一個目標數,S。現在你有兩個符號 + 和 -。對于數組中的任意一個整數,你都可以從 + 或 - 中選擇一個符號添加在前面。
返回可以使最終數組和為目標數 S 的所有添加符號的方法數。
示例 1:
輸入: nums: [1, 1, 1, 1, 1], S: 3 輸出: 5 解釋:>-1+1+1+1+1 = 3 +1-1+1+1+1 = 3 +1+1-1+1+1 = 3 +1+1+1-1+1 = 3 +1+1+1+1-1 = 3一共有5種方法讓最終目標和為3。注意:
1.數組非空,且長度不會超過20。
2.初始的數組的和不會超過1000。
3.保證返回的最終結果能被32位整數存下。
解題思路及實現
說實話這道題真沒想到使用 DFS 暴力解決,還是經驗太少了,這道題暴力解法是完全可以的,而且不會超時,因為題目中說了數組長度不會超過20,20個數字的序列,組合方式撐死了也就 2^20 種組合:
public class Solution {int count = 0;public int findTargetSumWays(int[] nums, int S) {dfs(nums, 0, 0, S);return count;}private void dfs(int[] nums, int index, int sum, int S) {if (index == nums.length) {if (sum == S) count++;} else {dfs(nums, index + 1, sum + nums[index], S);dfs(nums, index + 1, sum - nums[index], S);}} }4.二叉樹的中序遍歷
- 難度:Medium
題目描述
給定一個二叉樹,返回它的中序遍歷。
進階: 遞歸算法很簡單,你可以通過迭代算法完成嗎?
解題思路及實現
二叉樹相關真的是非常有趣的一個算法知識點(因為這道題非常具有代表性,我覺得面試考到的概率最高2333…),后續筆者會針對該知識點進行更詳細的探究,本文列出兩個解決方案。
1.遞歸法
public class Solution {// 1.遞歸法public List<Integer> inorderTraversal(TreeNode root) {List<Integer> list = new ArrayList<>();dfs(root, list);return list;}private void dfs(TreeNode node, List<Integer> list) {if (node == null) return;// 中序遍歷:左中右if (node.left != null)dfs(node.left, list);list.add(node.val);if (node.right != null)dfs(node.right, list);} }2.使用棧
public class Solution {// 2.使用棧public List<Integer> inorderTraversal(TreeNode root) {List<Integer> list = new ArrayList<>();Stack<TreeNode> stack = new Stack<>();TreeNode curr = root;while (!stack.isEmpty() || curr != null) {while (curr != null) {stack.push(curr);curr = curr.left;}curr = stack.pop();list.add(curr.val);curr = curr.right;}return list;} }參考 & 感謝
文章絕大部分內容節選自LeetCode,棧和深度優先搜索 概述:
- https://leetcode-cn.com/explore/learn/card/queue-stack/219/stack-and-dfs/
例題:
- https://leetcode-cn.com/problems/number-of-islands
- https://leetcode-cn.com/problems/clone-graph/
- https://leetcode-cn.com/problems/target-sum/
- https://leetcode-cn.com/problems/binary-tree-inorder-traversal/
關于我
Hello,我是 卻把清梅嗅 ,如果您覺得文章對您有價值,歡迎 ??,也歡迎關注我的 博客 或者 GitHub。
如果您覺得文章還差了那么點東西,也請通過關注督促我寫出更好的文章——萬一哪天我進步了呢?
- 我的Android學習體系
- 關于文章糾錯
- 關于知識付費
- 關于《反思》系列
總結
以上是生活随笔為你收集整理的栈和深度优先搜索(DFS)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 线性系统实验:化学方程式配平 与 天体轨
- 下一篇: R 实现线性判别分析教程