主要收录深度优先遍历和宽度优先遍历,深度优先遍历一般可以与回溯、递归、树等方法联用,达到优雅遍历的效果,而宽度优先搜索可以用到最短路问题的求解中。
为什么不用 bfs
去遍历?第一是因为 bfs
写起来麻烦,不如 dfs
直观。第二是在某些查找到满足情况即可退出的应用而言,bfs
需要一层一层的去检查,效率很低。
为什么不用 dfs
去求最短路?如上所示,bfs
可以一层一层的检查,相对 dfs
更容易查到最短路。
dfs 岛屿问题 130. 被围绕的区域 给你一个 m x n
的矩阵 board
,由若干字符 'X'
和 'O'
,找到所有被 'X'
围绕的区域,并将这些区域里所有的 'O'
用 'X'
填充。
那么如何填充内部的 O
呢?这里就要用到 dfs
,首先遍历 board
,如果遇到了 O
,那个和这个 O
相邻的 O
也要被填充,此时就要使用 dfs
来查找相邻的 O
由于只填充被 X
包围的 O
,因此,边界上的 O
不能被填充。那么我们预先把和边界相连的 O
都填充为其他符号,在处理完 board
内部的 O
的时候,在把其他符号替换为 O
即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 class Solution {public : int n, m; void solve (vector<vector<char >>& board) { n = board.size (); m = board[0 ].size (); for (int i = 0 ; i < n; i++) { dfs (i, 0 , board, '+' ); dfs (i, m-1 , board, '+' ); } for (int i = 0 ; i < m; i++) { dfs (0 , i, board, '+' ); dfs (n-1 , i, board, '+' ); } for (int i = 0 ; i < n; i++) { for (int j = 0 ; j < m; j++) { if (board[i][j] == 'O' ) { dfs (i, j, board, 'X' ); } } } for (int i = 0 ; i < n; i++) { for (int j = 0 ; j < m; j++) { if (board[i][j] == '+' ) { board[i][j] = 'O' ; } } } } void dfs (int r, int c, vector<vector<char >>& board, char pad) { if (r < 0 || c < 0 || r >= n || c >= m) { return ; } if (board[r][c] == 'O' ) { board[r][c] = pad; dfs (r + 1 , c, board, pad); dfs (r - 1 , c, board, pad); dfs (r, c + 1 , board, pad); dfs (r, c - 1 , board, pad); } } };
bfs 111. 二叉树的最小深度 给定一个二叉树,找出其最小深度。最小深度是从根节点到最近叶子节点的最短路径上的节点数量。说明:叶子节点是指没有子节点的节点。
求二叉树或者多叉树中根节点到叶子节点的最短路径,一般都是 bfs
遍历算法。给出 bfs
的模板:
1 2 3 4 5 6 7 8 9 10 11 12 queue.push (root); while (queue.size ()) { int s = queue.size (); for (int i = 0 ; i < s; i++) { auto node = queue.top (); queue.pop (); if (node->left != null) queue.push (node->left); if (node->right != null) queue.push (node->right); } }
在进入 bfs
之前先处理一些极端的特殊情况,比如根节点为空,根节点就是目标节点等
在处理完特殊情况后,之后就是 bfs
遍历,如果遍历期间的节点满足目标情况,返回结果即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 class Solution {public : int minDepth (TreeNode* root) { if (root == nullptr ) return 0 ; if (root->left == nullptr && root->right == nullptr ) return 1 ; int depth = 1 ; deque<TreeNode*> q; q.push_back (root); while (q.size ()) { int s = q.size (); for (int i = 0 ; i < s; i++) { auto node = q.front (); q.pop_front (); if (node->left == nullptr && node->right == nullptr ) { return depth; } if (node->left != nullptr ) q.push_back (node->left); if (node->right != nullptr ) q.push_back (node->right); } depth += 1 ; } return depth; } };
752. 打开转盘锁 你有一个带有四个圆形拨轮的转盘锁。每个拨轮都有 10 个数字: ‘0’, ‘1’, ‘2’, ‘3’, ‘4’, ‘5’, ‘6’, ‘7’, ‘8’, ‘9’ 。每个拨轮可以自由旋转:例如把 ‘9’ 变为 ‘0’,’0’ 变为 ‘9’ 。每次旋转都只能旋转一个拨轮的一位数字。
锁的初始数字为 ‘0000’ ,一个代表四个拨轮的数字的字符串。列表 deadends
包含了一组死亡数字,一旦拨轮的数字和列表里的任何一个元素相同,这个锁将会被永久锁定,无法再被旋转。
字符串 target
代表可以解锁的数字,你需要给出解锁需要的最小旋转次数,如果无论如何不能解锁,返回 -1 。示例:
1 2 3 4 5 6 输入:deadends = ["0201","0101","0102","1212","2002"], target = "0202" 输出:6 解释: 可能的移动序列为 "0000" -> "1000" -> "1100" -> "1200" -> "1201" -> "1202" -> "0202"。 注意 "0000" -> "0001" -> "0002" -> "0102" -> "0202" 这样的序列是不能解锁的, 因为当拨动到 "0102" 时这个锁就会被锁定。
寻找最短路径时,同样需要使用 bfs
算法。我们把这个问题看成一个多叉树问题,如果 00
是根节点,那么叶子节点就是 01, 10, 09, 90
,同理,也能得到 0000
为根节点时对应的叶子节点
同样,先处理极端情况,如根节点被死锁,以及根节点就是目标的特殊情况
之后,以 0000
为根节点开始 bfs
算法,我们手写两个函数,分别为 _up
和 _down
来对 0000
的每一位进行转动进而得到子节点,如果子节点满足要求,返回此时的深度即可
与二叉树不同的是,二叉树使用 root->left, root->right
能保证不会遍历重复节点,而对于此问题,很有可能从 0000
查找到 5555
,又从 5555
查找回 0000
,因为只要一直转动下去,0000
也是 5555
的子节点。因此,在遍历期间需要设置一个 map
,将遍历过的节点添加进去,保证不会重复遍历一个节点,不走回头路。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 class Solution {public : string _up(string node, int idx) { string s = node; if (s[idx] == '9' ) s[idx] = '0' ; else s[idx] += 1 ; return s; } string _down(string node, int idx) { string s = node; if (s[idx] == '0' ) s[idx] = '9' ; else s[idx] -= 1 ; return s; } int openLock (vector<string>& deadends, string target) { deque<string> q; q.push_back ("0000" ); unordered_map<string, int > m; for (auto i : deadends) m[i] = 10 ; if (m["0000" ] == 10 ) return -1 ; int cnt = 1 ; if (target == "0000" ) return 0 ; while (q.size ()) { int s = q.size (); for (int i = 0 ; i < s; i++) { auto node = q.front (); q.pop_front (); for (int i = 0 ; i < 4 ; i++) { string s1 = _up(node, i); if (s1 == target) return cnt; if (m[s1] != 10 ) { m[s1] = 10 ; q.push_back (s1); } string s2 = _down(node, i); if (s2 == target) return cnt; if (m[s2] != 10 ) { m[s2] = 10 ; q.push_back (s2); } } } cnt += 1 ; } return -1 ; } };