栈和深度优先搜索(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)相关推荐
- 一文搞定深度优先搜索(DFS)与广度优先搜索(BFS)【含完整源码】
写在前面:博主是一位普普通通的19届双非软工在读生,平时最大的爱好就是听听歌,逛逛B站.博主很喜欢的一句话花开堪折直须折,莫待无花空折枝:博主的理解是头一次为人,就应该做自己想做的事,做自己不后悔的事 ...
- C++实现深度优先搜索DFS(附完整源码)
C++实现深度优先搜索DFS C++实现深度优先搜索DFS完整源码(定义,实现,main函数测试) C++实现深度优先搜索DFS完整源码(定义,实现,main函数测试) #include <al ...
- C++用stack实现深度优先搜索DFS(附完整源码)
C++用stack实现深度优先搜索DFS的实现 C++用stack实现深度优先搜索DFS的完整源码(定义,实现,main函数测试) C++用stack实现深度优先搜索DFS的完整源码(定义,实现,ma ...
- 深度优先搜索(DFS) 总结(算法+剪枝+优化总结)
深度优先搜索(DFS) 总结(算法+剪枝+优化总结) 本文中会引用部分实例.文献资料来自不同的作者之手,由于资料整理比较困难,转载地址不在文中列举.如有侵权请联系我更换或删除!对于提供题解思路的各位大 ...
- 【算法很美】深入递归 (下)深度优先搜索DFS问题
深搜.回溯.剪枝 深度优先搜索DFS 2.1 无死角搜索I 数独游戏 部分和 水洼数目 2.2 回溯和剪枝 n皇后问题 素数环 困难的串 小结 一些使用 2.1 无死角搜索I 数独游戏 你一定听说过& ...
- 【算法入门】深度优先搜索(DFS)
深度优先搜索(DFS) [算法入门] 郭志伟@SYSU:raphealguo(at)qq.com 2012/05/12 1.前言 深度优先搜索(缩写DFS)有点类似广度优先搜索,也是对一个连通图进行遍 ...
- matlab bfs函数,matlab练习程序(广度优先搜索BFS、深度优先搜索DFS)
如此经典的算法竟一直没有单独的实现过,真是遗憾啊. 广度优先搜索在过去实现的二值图像连通区域标记和prim最小生成树算法时已经无意识的用到了,深度优先搜索倒是没用过. 这次单独的将两个算法实现出来,因 ...
- 【数据结构与算法】2.深度优先搜索DFS、广度优先搜索BFS
原文链接:https://blog.csdn.net/qq_41681241/article/details/81432634 总结 一般来说,广搜常用于找单一的最短路线,或者是规模小的路径搜索,它的 ...
- 树的广度优先搜索(BFS),深度优先搜索(DFS)
BFS:Breadth First Search,广度优先搜索 DFS:Depth First Search,深度优先搜索 如图,A节点的下一级元素为B节点和C节点,B节点的下一级元素为D节点和E节点 ...
最新文章
- angular4 下载文件 Excel zip包
- Nature子刊 | 研究人员提出神经脆性可作为癫痫发作区(SOZ)的脑电图(EEG)标志物
- php 系统日志,PHP中把错误日志保存在系统日志中(Windows系统)
- 《Cracking the Coding Interview》——第16章:线程与锁——题目3
- PythonWeb仿51edu项目实战篇视频教程教学视频
- 2020邮箱账号密码大全_通知 | 复旦大学2020年春季学期研究生选课FAQ
- 写游戏软件要学什么_为什么要写关于您所知道的(或所学到的)的内容
- 万亿级新基建战场,阿里云的安全“防线”
- java框架_这篇让你吃透Java集合框架!
- 如何将zipoutputstream返回_嫦娥五号返回器要“跳”一下再回地球!这一“跳”大有讲究...
- 即时获取最新全国省市区县地图json数据(亲测可用)以及echarts中使用中国地图
- gtool:操作genotype data的利器
- PS纯色图标更换颜色
- 《Adaptive Unimodal Cost Volume Filtering for Deep Stereo Matching》
- 涨薪申请多次被拒,去年我带的实习生,今年工资比我还高1K,当天就裸辞走了.....
- JavaScript自定义tirm方法
- MATLAB画三维墨西哥草帽图,数字图像处理之图像分割-20210401063921.ppt-原创力文档...
- 10分钟在服务器上搭建WordPress
- CSDN日报191105:2019年11月全国程序员工资统计,区块链工程师比算法工资高
- MATLAB 基础知识 数据类型 元胞数组 创建元胞数组
热门文章
- Halcon之单相机标定
- Antimalware Service Executable 彻底关闭 没有繁琐的操作,一步到位,亲测有效!!!
- pms输变电状态监测_输变电设备状态监测系统(国网电科院).pdf
- 生物计算机是未来型计算机吗,未来的新型计算机就是指生物计算机、量子计算机和光计算机。...
- Codeforces Round #566 (Div. 2)-E. Product Oriented Recurrence
- Antd多文件上传后台接收为null问题
- LInux 开机启动之MBR引导
- 高速数据采集卡如何在高速下进行采集
- 算极化率的格林函数算法
- 2022-2028全球与中国制药二次包装设备市场现状及未来发展趋势