一、理论基础
- 图
- DFS:一个方向一直搜,搜不下去了,再换方向
public void dfs(参数) { if (终止条件) { 存放结果; return; } for (选择:本节点所连接的其他节点){ 处理节点 dfs(图,选择的节点); // 递归 回溯,撤销处理结果 } }
- BFS:先把本节点所连接的所有节点遍历一遍,走到下一个节点的时候,再把连接节点的所有节点遍历一遍。
public void bfs (){ Deque<> deque = new LinkedList<>(); deque初始化 while (!deque.isEmpty()) { for (){ 处理本层数据 } } }
二、所有可能的路径
-
题目
- 给你一个有
n
个节点的 有向无环图(DAG),请你找出所有从节点0
到节点n-1
的路径并输出(不要求按特定顺序)
- 给你一个有
-
思路
- DFS+回溯
-
代码
class Solution { List<List<Integer>> lists = new ArrayList<>(); List<Integer> list = new ArrayList<>(); public List<List<Integer>> allPathsSourceTarget(int[][] graph) { // dfs list.add(0); dfs(0, graph); return lists; } public void dfs(int start, int[][] graph) { if (start == graph.length - 1) { lists.add(new ArrayList<>(list)); return; } for (int i = 0; i < graph[start].length; i++) { list.add(graph[start][i]); dfs(graph[start][i], graph); list.remove(list.size() - 1); } } }
三、岛屿的数量
-
题目
-
给你一个由
'1'
(陆地)和'0'
(水)组成的的二维网格,请你计算网格中岛屿的数量。岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。此外,你可以假设该网格的四条边均被水包围。
-
-
思路
- DFS/BFS
-
代码
class Solution { public int numIslands(char[][] grid) { int res = 0; for (int i = 0; i < grid.length; i++) { for (int j = 0; j < grid[0].length; j++) { if (grid[i][j] == '0') { continue; } res++; bfs(i, j, grid); } } return res; } // dfs public void dfs(int row, int col, char[][] grid) { if (row < 0 || col < 0 || row >= grid.length || col >= grid[0].length || grid[row][col] == '0') { return; } grid[row][col] = '0'; dfs(row + 1, col, grid); dfs(row - 1, col, grid); dfs(row, col + 1, grid); dfs(row, col - 1, grid); } // BFS public void bfs(int row, int col, char[][] grid) { Deque<int[]> deque = new LinkedList<>(); deque.offer(new int[] { row, col }); while (!deque.isEmpty()) { for (int i = deque.size(); i > 0; i--) { int[] poll = deque.poll(); int a = poll[0]; int b = poll[1]; if (a < 0 || b < 0 || a >= grid.length || b >= grid[0].length || grid[a][b] == '0') { continue; } grid[a][b] = '0'; deque.offer(new int[] { a + 1, b }); deque.offer(new int[] { a - 1, b }); deque.offer(new int[] { a, b + 1 }); deque.offer(new int[] { a, b - 1 }); } } } }
四、岛屿的最大面积
-
题目
-
给你一个大小为
m x n
的二进制矩阵grid
。岛屿 是由一些相邻的1
(代表土地) 构成的组合,这里的「相邻」要求两个1
必须在 水平或者竖直的四个方向上 相邻。你可以假设grid
的四个边缘都被0
(代表水)包围着。岛屿的面积是岛上值为1
的单元格的数目。计算并返回grid
中最大的岛屿面积。如果没有岛屿,则返回面积为0
。
-
-
思路
-
代码
class Solution { public int maxAreaOfIsland(int[][] grid) { int res = 0; for (int i = 0; i < grid.length; i++) { for (int j = 0; j < grid[0].length; j++) { if (grid[i][j] == 0) { continue; } res = Math.max(res, bfs(i, j, grid)); } } return res; } public int dfs(int row, int col, int[][] grid) { if (row < 0 || col < 0 || row >= grid.length || col >= grid[0].length || grid[row][col] == 0) { return 0; } grid[row][col] = 0; return 1 + dfs(row + 1, col, grid) + dfs(row - 1, col, grid) + dfs(row, col + 1, grid) + dfs(row, col - 1, grid); } public int bfs(int row, int col, int[][] grid) { int[][] dir = new int[][] { { 1, 0 }, { -1, 0 }, { 0, 1 }, { 0, -1 } }; Deque<int[]> deque = new LinkedList<>(); deque.offer(new int[] { row, col }); grid[row][col] = 0; int res = 1; while (!deque.isEmpty()) { int[] poll = deque.poll(); for (int i = 0; i < 4; i++) { int a = poll[0] + dir[i][0]; int b = poll[1] + dir[i][1]; if (a < 0 || b < 0 || a >= grid.length || b >= grid[0].length || grid[a][b] == 0) { continue; } grid[a][b] = 0; res++; deque.offer(new int[]{a, b}); } } return res; } }
五、飞地的数量
-
题目
-
给你一个大小为
m x n
的二进制矩阵grid
,其中0
表示一个海洋单元格、1
表示一个陆地单元格。一次 移动 是指从一个陆地单元格走到另一个相邻(上、下、左、右)的陆地单元格或跨过grid
的边界。返回网格中 无法 在任意次数的移动中离开网格边界的陆地单元格的数量。
-
-
思路
-
代码
class Solution { boolean mark = false; int temp = 0; public int numEnclaves(int[][] grid) { int res = 0; for (int i = 0; i < grid.length; i++) { for (int j = 0; j < grid[0].length; j++) { if (grid[i][j] == 1){ dfs(i, j, grid); if (!mark){ res += temp; } } mark = false; temp = 0; } } return res; } private void dfs(int x, int y, int[][] grid) { if (x < 0 || x > grid.length - 1 || y < 0 || y > grid[0].length - 1) { mark = true; return; } if (grid[x][y] == 0) { return; } grid[x][y] = 0; temp++; dfs(x + 1, y, grid); dfs(x - 1, y, grid); dfs(x, y + 1, grid); dfs(x, y - 1, grid); } }
六、被围绕的区域
-
题目
- 给你一个
m x n
的矩阵board
,由若干字符'X'
和'O'
,找到所有被'X'
围绕的区域,并将这些区域里所有的'O'
用'X'
填充。
- 给你一个
-
思路
- 从地图周边出发,将周边空格相邻的'O'都做上标记
-
代码
class Solution { char[][] res; public void solve(char[][] board) { res = new char[board.length][board[0].length]; for (char[] r : res) { Arrays.fill(r, 'X'); } for (int i = 0; i < board.length; i++) { for (int j = 0; j < board[0].length; j++) { if (board[i][j] == 'O' && (i == 0 || j == 0 || i == board.length - 1 || j == board[0].length - 1)) { dfs(i, j, board); } } } for (int i = 0; i < board.length; i++) { System.arraycopy(res[i], 0, board[i], 0, board[0].length); } } public void dfs(int row, int col, char[][] board) { if (row < 0 || col < 0 || row >= board.length || col >= board[0].length || board[row][col] == 'X') { return; } board[row][col] = 'X'; res[row][col] = 'O'; dfs(row + 1, col, board); dfs(row - 1, col, board); dfs(row, col + 1, board); dfs(row, col - 1, board); } }
七、太平洋大西洋水流问题
-
题目
- 既可流向太平洋也可流向大西洋
-
思路
- 从太平洋边上的节点 逆流而上,将遍历过的节点都标记上。
- 从大西洋的边上节点 逆流而长,将遍历过的节点也标记上。
- 然后两方都标记过的节点就是既可以流太平洋也可以流大西洋的节点。
-
代码
class Solution { int[][] dir = new int[][] { { 1, 0 }, { -1, 0 }, { 0, 1 }, { 0, -1 } }; public List<List<Integer>> pacificAtlantic(int[][] heights) { List<List<Integer>> list = new ArrayList<>(); boolean[][] usedA = new boolean[heights.length][heights[0].length]; boolean[][] usedP = new boolean[heights.length][heights[0].length]; for (int i = 0; i < heights.length; i++) { dfs(i, 0, heights, usedP); dfs(i, heights[0].length - 1, heights, usedA); } for (int j = 0; j < heights[0].length; j++) { dfs(0, j, heights, usedP); dfs(heights.length - 1, j, heights, usedA); } for (int i = 0; i < heights.length; i++) { for (int j = 0; j < heights[0].length; j++) { if (usedA[i][j] && usedP[i][j]) { int a = i; int b = j; list.add(new ArrayList<>() { { add(a); add(b); } }); } } } return list; } public void dfs(int row, int col, int[][] heights, boolean[][] used) { if (used[row][col]) { return; } used[row][col] = true; for (int i = 0; i < 4; i++) { int a = row + dir[i][0]; int b = col + dir[i][1]; if (a < 0 || b < 0 || a > heights.length - 1 || b > heights[0].length - 1 || heights[row][col] > heights[a][b]) { continue; } dfs(a, b, heights, used); } } }
八、最大人工岛
-
题目
-
给你一个大小为
n x n
二进制矩阵grid
。最多 只能将一格0
变成1
。返回执行此操作后,grid
中最大的岛屿面积是多少?
-
-
思路
- 标记每个岛屿并记录面积
- 寻找为0的,搜索周围岛屿
-
代码
class Solution { int temp = 2; int[][] dir = new int[][] { { 1, 0 }, { -1, 0 }, { 0, 1 }, { 0, -1 } }; public int largestIsland(int[][] grid) { Map<Integer, Integer> map = new HashMap<>(); map.put(0, 0); // 岛屿变成面积 for (int i = 0; i < grid.length; i++) { for (int j = 0; j < grid[0].length; j++) { if (grid[i][j] == 1) { map.put(temp, dfs(i, j, grid)); temp++; } } } int res = map.get(grid[0][0]); for (int i = 0; i < grid.length; i++) { for (int j = 0; j < grid[0].length; j++) { if (grid[i][j] == 0) { int temp = 1; Set<Integer> set = new HashSet<>(); for (int k = 0; k < 4; k++) { int a = i + dir[k][0]; int b = j + dir[k][1]; if (a < 0 || b < 0 || a > grid.length - 1 || b > grid[0].length - 1) { continue; } set.add(grid[a][b]); } for (int s : set) { temp += map.get(s); } res = Math.max(res, temp); } } } return res; } public int dfs(int row, int col, int[][] grid) { if (row < 0 || col < 0 || row > grid.length - 1 || col > grid[0].length - 1 || grid[row][col] != 1) { return 0; } grid[row][col] = temp; return dfs(row + 1, col, grid) + dfs(row - 1, col, grid) + dfs(row, col + 1, grid) + dfs(row, col - 1, grid) + 1; } }
九、单词接龙
-
题目
- 字典
wordList
中从单词beginWord
和endWord
的 转换序列 是一个按下述规格形成的序列beginWord -> s1 -> s2 -> ... -> sk
- 字典
-
思路
- 点与点差一个字符便可建立连接关系
- 无向图求最短路,广搜最为合适,广搜只要搜到了终点,那么一定是最短的路径。因为广搜就是以起点中心向四周扩散的搜索。
-
代码
-
class Solution { // 最短路径BFS public int ladderLength(String beginWord, String endWord, List<String> wordList) { if (!wordList.contains(endWord)) { return 0; } Set<String> set = new HashSet<>(wordList); Map<String, Integer> map = new HashMap<>(); Deque<String> deque = new LinkedList<>(); map.put(beginWord, 1); deque.offer(beginWord); while (!deque.isEmpty()) { String poll = deque.poll(); int path = map.get(poll); for (int i = 0; i < poll.length(); i++) { char[] chs = poll.toCharArray(); for (char j = 'a'; j <= 'z'; j++) { chs[i] = j; String newWord = String.valueOf(chs); if (newWord.equals(endWord)) { return path + 1; } if (set.contains(newWord) && !map.containsKey(newWord)) { deque.offer(newWord); map.put(newWord, path + 1); } } } } return 0; } }
-
十、钥匙和房间
-
题目
-
思路
- 有向图全路径
-
代码
class Solution { boolean[] used; public boolean canVisitAllRooms(List<List<Integer>> rooms) { used = new boolean[rooms.size()]; used[0] = true; dfs(0, rooms); for (boolean use : used) { if (!use) { return false; } } return true; } public void dfs(int start, List<List<Integer>> rooms) { for (int index : rooms.get(start)) { if (used[index]) { continue; } used[index] = true; dfs(index, rooms); } } }
十一、岛屿的周长
-
题目
-
思路
-
代码
class Solution { int[][] dir = new int[][] { { 1, 0 }, { -1, 0 }, { 0, 1 }, { 0, -1 } }; public int islandPerimeter(int[][] grid) { int res = 0; for (int i = 0; i < grid.length; i++) { for (int j = 0; j < grid[0].length; j++) { if (grid[i][j] == 0) { continue; } for (int k = 0; k < 4; k++) { int a = i + dir[k][0]; int b = j + dir[k][1]; if (a < 0 || b < 0 || a > grid.length - 1 || b > grid[0].length - 1 || grid[a][b] == 0) { res++; } } } } return res; } }