日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

栈和深度优先搜索(DFS)

發布時間:2023/12/20 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 栈和深度优先搜索(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/

解題思路及實現

題目比較難理解,需要注意的是:

  • 因為是 深拷貝 ,因此所有節點都需要通過 new 進行實例化,即需要遍歷圖中的每個節點,因此解決方案就浮現而出了,使用 DFS 或者 BFS 即可;
  • 對每個已經復制過的節點進行標記,避免無限循環導致堆棧的溢出。
  • 用 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)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。