代碼模板
BFS模板
def BFS(graph, start, end):
visited = set()
queue = []
queue.append([start])
while queue:
node = queue.pop()
visited.add(node)
process(node)
nodes = generate_related_nodes(node)
queue.push(nodes)
# other processing work
...
DFS模板
遞歸玩法
visited = set()
def dfs(node, visited):
if node in visited: # terminator
# already visited
return
visited.add(node)
# process current node here.
...
for next_node in node.children():
if next_node not in visited:
dfs(next_node, visited)
非遞歸玩法
def DFS(self, tree):
if tree.root is None:
return []
visited, stack = [], [tree.root]
while stack:
node = stack.pop()
visited.add(node)
process (node)
nodes = generate_related_nodes(node)
stack.push(nodes)
# other processing work
...
二叉樹的層次遍歷
BFS的高頻經典題目
BFS解法
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
if (root == null) return new ArrayList<>();
LinkedList<TreeNode> queue = new LinkedList<>();
List<List<Integer>> res = new ArrayList<>();
queue.offerLast(root);
while (!queue.isEmpty()) {
int size = queue.size();
List<Integer> tmpList = new ArrayList<>();
for (int i = 0; i < size; i++) {
TreeNode cur = queue.pollFirst();
tmpList.add(cur.val);
if (cur.left != null) queue.offerLast(cur.left);
if (cur.right != null) queue.offerLast(cur.right);
}
res.add(tmpList);
}
return res;
}
}
雖然是經典的BFS題目,但是用DFS也可以做出來
DFS解法
public class Solution {
List<List<Integer>> res;
public List<List<Integer>> levelOrder(TreeNode root) {
if (root == null) return new LinkedList<>();
res = new LinkedList<>();
dfs(root, 0);
return res;
}
private void dfs(TreeNode root, int depth) {
if (root == null) return;
if (depth >= res.size()) res.add(new LinkedList<>());
res.get(depth).add(root.val);
dfs(root.left, depth + 1);
dfs(root.right, depth + 1);
}
}
括號生成
DFS解法:
public class Solution {
List<String> res;
int n;
public List<String> generateParenthesis(int n) {
res = new ArrayList<>();
this.n = n;
dfs(0, 0, "");
return res;
}
/**
* @param left 左括號個數
* @param right 右括號個數
* @param s 結果字符串
*/
private void dfs(int left, int right, String s) {
//退出條件
if (left == n && right == n) {
res.add(s);
return;
}
//可以生成左括號的條件:左括號小於n
if (left < n) dfs(left + 1, right, s + "(");
//可以生成右括號的條件:右括號小於左括號
if (left > right) dfs(left, right + 1, s + ")");
//reserve
}
}
在每個樹行中找最大值
和二叉樹的中序遍歷做法幾乎一模一樣,只需要修改結果集產生的方式
BFS解法
public class Solution {
public List<Integer> largestValues(TreeNode root) {
if (root == null) return new ArrayList<>();
List<Integer> res = new ArrayList<>();
Deque<TreeNode> deque = new LinkedList<TreeNode>();
deque.offerLast(root);
while (!deque.isEmpty()) {
int size = deque.size();
int lineMax = Integer.MIN_VALUE;
for (int i = 0; i < size; i++) {
TreeNode cur = deque.pollFirst();
lineMax = Math.max(lineMax, cur.val);
if (cur.left != null) deque.offerLast(cur.left);
if (cur.right != null) deque.offerLast(cur.right);
}
res.add(lineMax);
}
return res;
}
}
島嶼數量
DFS解法
遍歷二維數組,當發現1之後,計數+1,然後調用函數dfs
遞歸的去將這個1的所有鄰接的1都變爲0,然後繼續搜索
public class Solution {
public int numIslands(char[][] grid) {
if (grid == null || grid.length == 0 || grid[0].length == 0) return 0;
int count = 0;
int rows = grid.length;
int cols = grid[0].length;
for (int r = 0; r < rows; r++) {
for (int c = 0; c < cols; c++) {
if (grid[r][c] == '1') {
count++;
//發現島嶼,然後廣度優先搜索臨界的區域,全部變成海洋
dfs(r, c, rows, cols, grid);
}
}
}
return count;
}
private void dfs(int r, int c, int rows, int cols, char[][] grid) {
if (r == -1 || c == -1 || r == rows || c == cols || grid[r][c] != '1') return;
//將當前1標記爲0
grid[r][c] = 0;
//向上左右進行擴展
dfs(r + 1, c, rows, cols, grid);
dfs(r - 1, c, rows, cols, grid);
dfs(r, c + 1, rows, cols, grid);
dfs(r, c - 1, rows, cols, grid);
}
}
BFS解法
public class Solution {
public int numIslands(char[][] grid) {
if (grid == null || grid.length == 0 || grid[0].length == 0) return 0;
int count = 0;
int rows = grid.length;
int cols = grid[0].length;
for (int r = 0; r < rows; r++) {
for (int c = 0; c < cols; c++) {
if (grid[r][c] == '1') {
count++;
//發現島嶼,然後廣度優先搜索臨界的區域,全部變成海洋
bfs(r, c, rows, cols, grid);
}
}
}
return count;
}
private void bfs(int r, int c, int rows, int cols, char[][] grid) {
Deque<Integer> deque = new LinkedList<>();
//將二維數組換爲一維進行存儲
deque.offer(r * cols + c);
while (!deque.isEmpty()) {
Integer cur = deque.poll();
int row = cur / cols;
int col = cur % cols;
//如果已經遍歷過了,就不再重複
if (grid[row][col] == '0') {
continue;
}
grid[row][col] = '0';
if (row != (rows - 1) && grid[row + 1][col] == '1') {
deque.offer((row + 1) * cols + col);
}
if (col != (cols - 1) && grid[row][col + 1] == '1') {
deque.offer(row * cols + col + 1);
}
if (row != 0 && grid[row - 1][col] == '1') {
deque.offer((row - 1) * cols + col);
}
if (col != 0 && grid[row][col - 1] == '1') {
deque.offer(row * cols + col - 1);
}
}
}
}
島嶼的最大面積
解決方案和上一個題島嶼數量一致,遍歷整個數組,發現1之後,採用dfs或者bfs進行沉島
DFS解法
public class Solution {
public int maxAreaOfIsland(int[][] grid) {
if (grid == null || grid.length == 0 || grid[0].length == 0) return 0;
int maxSize = 0;
int rows = grid.length;
int cols = grid[0].length;
for (int r = 0; r < rows; r++) {
for (int c = 0; c < cols; c++) {
if (grid[r][c] == 1) {
maxSize = Math.max(maxSize, dfs(r, c, rows, cols, grid));
}
}
}
return maxSize;
}
private Integer dfs(int r, int c, int rows, int cols, int[][] grid) {
if (r == -1 || c == -1 || r == rows || c == cols || grid[r][c] == 0) {
return 0;
}
//將訪問過的島嶼弄成0
grid[r][c] = 0;
int num = 1;
//每個方向都進行遍歷,遍歷的結果進行累加就是最終的面積
num += dfs(r + 1, c, rows, cols, grid);
num += dfs(r, c + 1, rows, cols, grid);
num += dfs(r - 1, c, rows, cols, grid);
num += dfs(r, c - 1, rows, cols, grid);
return num;
}
}
被圍繞的區域
思路:
- 遍歷查找邊界‘O’
- 讓邊界’O’採用搜索算法(bfs or dfs)查找鄰接‘O‘
- 將邊界‘O’和他們的鄰接‘O’都設置成‘#’
- 剩下的‘O’就都是圍繞的了
- 遍歷數組,將所有的‘O‘修改成‘X’,將所有的‘#’修改成‘O‘
DFS解法
public class Solution {
public void solve(char[][] board) {
if (board == null || board.length == 0 || board[0].length == 0) return;
int m = board.length;
int n = board[0].length;
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
boolean isEdge = i == 0 || j == 0 || i == m - 1 || j == n - 1;
if (isEdge && board[i][j] == 'O')
dfs(board, i, j);
}
}
//將與邊界'O'有段的'O'標記之後
//將‘O’轉換爲‘X’
//將‘#’轉換爲‘O’
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (board[i][j] == 'O') board[i][j] = 'X';
if (board[i][j] == '#') board[i][j] = 'O';
}
}
}
private void dfs(char[][] board, int i, int j) {
if (i == -1 || j == -1
|| i == board.length || j == board[0].length
|| board[i][j] == 'X' || board[i][j] == '#') {
return;
}
//將邊界‘O’和與邊界‘O’相鄰的‘O’都設置爲#
board[i][j] = '#';
dfs(board, i - 1, j);
dfs(board, i + 1, j);
dfs(board, i, j - 1);
dfs(board, i, j + 1);
}
}
單詞接龍
BFS解法1
用一個boolean數組記錄字典中的字符串是否被訪問過了
標準的bfs遍歷模板(和二叉樹的層次遍歷相似),只是加上了一些條件的判斷
- 如果beginWord在字典中,先標記爲訪問過,不需要再訪問
- 如果字符串被訪問過了,就不再進行訪問
- 如果訪問的字符串不滿足和當前串只差一個字符不同,也不再進行操作
- 當匹配上了endWord就直接返回(因爲是層序遍歷,每層的次數相同且由少到多,所以第一次發現一定是最短的)
public class Solution {
public int ladderLength(String beginWord, String endWord, List<String> wordList) {
if (!wordList.contains(endWord)) {
return 0;
}
//記錄是否訪問過
boolean[] visited = new boolean[wordList.size()];
//檢驗是否存在beginWord,如果存在,就置爲訪問過了,沒必要訪問
int index = wordList.indexOf(beginWord);
if (index != -1) {
visited[index] = true;
}
//bfs暫存隊列
Queue<String> queue = new LinkedList<>();
queue.offer(beginWord);
int count = 0;
while (!queue.isEmpty()) {
int size = queue.size();
count++;
while (size-- > 0) {
String start = queue.poll();
for (int i = 0; i < wordList.size(); i++) {
//訪問過了,跳過,訪問下一個
if (visited[i]) continue;
String s = wordList.get(i);
//不滿足和當前只差一個字符不同,跳過,訪問下一個
if (!canConvert(start, s)) {
continue;
}
//和endWord匹配上了,進行返回,因爲是bfs,所以找到了直接返回就是最短的
if (s.equals(endWord)) {
return count + 1;
}
//訪問完了將當前置爲已訪問
visited[i] = true;
//當前值壓棧
queue.offer(s);
}
}
}
return 0;
}
//判斷s1和s2是否只有一個字符不同
private boolean canConvert(String s1, String s2) {
int count = 0;
for (int i = 0; i < s1.length(); i++) {
if (s1.charAt(i) != s2.charAt(i)) {
count++;
if (count > 1) {//達到兩個不同了,說明不符合
return false;
}
}
}
//不同字符小於兩個,判斷是否是一個
return count == 1;
}
}
BFS解法2:雙向BFS
public class Solution {
public int ladderLength(String beginWord, String endWord, List<String> wordList) {
int end = wordList.indexOf(endWord);
if (end == -1) return 0;
wordList.add(beginWord);
int start = wordList.size() - 1;
//bfs暫存隊列
Queue<Integer> queue1 = new LinkedList<>();
Queue<Integer> queue2 = new LinkedList<>();
//訪問記錄set
Set<Integer> visited1 = new HashSet<>();
Set<Integer> visited2 = new HashSet<>();
queue1.offer(start);
queue2.offer(end);
visited1.add(start);
visited2.add(end);
int count = 0;
while (!queue1.isEmpty() && !queue2.isEmpty()) {
count++;
//判斷兩個隊列的長度,把短的令成1,然後把短的進行遍歷,加快速度
if (queue1.size() > queue2.size()) {//將兩個的隊列和set都交換下
Queue<Integer> tmpqueue = queue1;
queue1 = queue2;
queue2 = tmpqueue;
Set<Integer> tmpset = visited1;
visited1 = visited2;
visited2 = tmpset;
}
int size = queue1.size();
while (size-- > 0) {
String s = wordList.get(queue1.poll());
for (int i = 0; i < wordList.size(); i++) {
//訪問過了,跳過,訪問下一個
if (visited1.contains(i)) continue;
//不滿足和當前只差一個字符不同,跳過,訪問下一個
if (!canConvert(s, wordList.get(i))) {
continue;
}
//退出條件
if (visited2.contains(i)) {
return count + 1;
}
//訪問完了將當前置爲已訪問
visited1.add(i);
//當前值壓棧
queue1.offer(i);
}
}
}
return 0;
}
//判斷s1和s2是否只有一個字符不同
private boolean canConvert(String s1, String s2) {
int count = 0;
for (int i = 0; i < s1.length(); i++) {
if (s1.charAt(i) != s2.charAt(i)) {
if (++count > 1) {//達到兩個不同了,說明不符合
return false;
}
}
}
//不同字符小於兩個,判斷是否是一個
return count == 1;
}
}