一、深度优先搜索(DFS)

1、岛屿的最大面积

问题:

给你一个大小为 m x n 的二进制矩阵 grid 。

岛屿 是由一些相邻的 1 (代表土地) 构成的组合,这里的「相邻」要求两个 1 必须在 水平或者竖直的四个方向上 相邻。你可以假设 grid 的四个边缘都被 0(代表水)包围着。

岛屿的面积是岛上值为 1 的单元格的数目。

计算并返回 grid 中最大的岛屿面积。如果没有岛屿,则返回面积为 0 。

示例 1:

输入:grid = [[0,0,1,0,0,0,0,1,0,0,0,0,0],[0,0,0,0,0,0,0,1,1,1,0,0,0],[0,1,1,0,1,0,0,0,0,0,0,0,0],[0,1,0,0,1,1,0,0,1,0,1,0,0],[0,1,0,0,1,1,0,0,1,1,1,0,0],[0,0,0,0,0,0,0,0,0,0,1,0,0],[0,0,0,0,0,0,0,1,1,1,0,0,0],[0,0,0,0,0,0,0,1,1,0,0,0,0]]
输出:6
解释:答案不应该是 11 ,因为岛屿只能包含水平或垂直这四个方向上的 1 。
示例 2:

输入:grid = [[0,0,0,0,0,0,0,0]]
输出:0

 1 package LeetCode.test5_sousuosuanfa;
 2 
 3 public class ques_695_岛屿的最大面积 {
 4     public static void main(String[] args) {
 5         int[][] grid = {{0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0},
 6                 {0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0},
 7                 {0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0},
 8                 {0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0},
 9                 {0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0},
10                 {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0},
11                 {0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0},
12                 {0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0}};
13         System.out.println(maxAreaOfIsland(grid));
14     }
15 
16     public static int maxAreaOfIsland(int[][] grid) {
17         if (grid == null || grid.length == 0) {
18             return 0;
19         }
20         int[] X = {0, 0, 1, -1};
21         int[] Y = {1, -1, 0, 0};
22         int max_area = 0;
23         for (int i = 0; i < grid.length; i++) {
24             for (int j = 0; j < grid[0].length; j++) {
25                 if (grid[i][j] == 1) {
26                     System.out.println(DFS_maxAreaOfIsland(i, j, grid, X, Y));
27 //                    max_area = Math.max(DFS_maxAreaOfIsland(i, j, grid, X, Y), max_area);
28                 }
29             }
30         }
31         return max_area;
32     }
33 
34     public static int DFS_maxAreaOfIsland(int x, int y, int[][] grid, int[] X, int[] Y) {
35         int area = 1;
36         grid[x][y] = 0;
37         for (int i = 0; i < 4; i++) {
38             int newX = x + X[i];
39             int newY = y + Y[i];
40             if (newX >= 0 && newY >= 0 && newX < grid.length && newY < grid[0].length && grid[newX][newY] == 1) {
41                 area += DFS_maxAreaOfIsland(newX, newY, grid, X, Y);
42             }
43         }
44         return area;
45     }
46 }
View Code

2、省份数量

问题:

有 n 个城市,其中一些彼此相连,另一些没有相连。如果城市 a 与城市 b 直接相连,且城市 b 与城市 c 直接相连,那么城市 a 与城市 c 间接相连。

省份是一组直接或间接相连的城市,组内不含其他没有相连的城市。

给你一个 n x n 的矩阵 isConnected ,其中 isConnected[i][j] = 1表示第 i 个城市和第 j 个城市直接相连,而 isConnected[i][j] = 0 表示二者不直接相连。

返回矩阵中省份的数量。

示例 1:

输入:isConnected = [[1,1,0],[1,1,0],[0,0,1]]
输出:2
示例 2:

输入:isConnected = [[1,0,0],[0,1,0],[0,0,1]]
输出:3

 1 package LeetCode.test5_sousuosuanfa;
 2 
 3 public class ques_547_省份数量 {
 4     public static void main(String[] args) {
 5         int[][] isConnected = {{1, 0, 0, 1},
 6                 {0, 1, 1, 0},
 7                 {0, 1, 1, 1},
 8                 {1, 0, 1, 1}};
 9         System.out.println(findCircleNum(isConnected));
10     }
11 
12     public static int findCircleNum(int[][] isConnected) {
13         //新建一个visited数组表示当前城市是否被访问
14         boolean[] visited = new boolean[isConnected.length]; // 默认为false
15         int res = 0; //计数
16         for (int i = 0; i < isConnected.length; i++) {
17             if (!visited[i]) {
18                 res++;  //找到一个没有访问的城市时则视为找到一个省份,再递归将所有与其相连城市设置为访问状态
19                 DFS_findCircleNum(isConnected, visited, i);
20             }
21         }
22         return res;
23     }
24 
25     public static void DFS_findCircleNum(int[][] isConnected, boolean[] visited, int i) {
26         visited[i]=true;  //将当前探索城市设置为已访问状态
27         for (int j = 0; j < isConnected.length; j++) {
28             if (isConnected[i][j]==1&& !visited[j]){
29                 //当当前城市A与其他城市B相连接且B没有被访问到时继续递归寻找城市
30                 DFS_findCircleNum(isConnected,visited,j);
31             }
32         }
33     }
34 }
View Code

3、太平洋大西洋水流问题

问题:

给定一个 m x n 的非负整数矩阵来表示一片大陆上各个单元格的高度。“太平洋”处于大陆的左边界和上边界,而“大西洋”处于大陆的右边界和下边界。

规定水流只能按照上、下、左、右四个方向流动,且只能从高到低或者在同等高度上流动。

请找出那些水流既可以流动到“太平洋”,又能流动到“大西洋”的陆地单元的坐标。

示例:

给定下面的 5x5 矩阵:

heights = {{1, 2, 2, 3, 5},

    {3, 2, 3, 4, 4},

    {2, 4, 5, 3, 1},

    {6, 7, 1, 4, 5},

    {5, 1, 1, 2, 4}};

返回:

[[0, 4], [1, 3], [1, 4], [2, 2], [3, 0], [3, 1], [4, 0]] 

 1 package LeetCode.test5_sousuosuanfa;
 2 
 3 import java.util.ArrayList;
 4 import java.util.List;
 5 
 6 public class ques_417_太平洋大西洋水流问题 {
 7     public static void main(String[] args) {
 8         int[][] heights = {{1, 2, 2, 3, 5},
 9                 {3, 2, 3, 4, 4},
10                 {2, 4, 5, 3, 1},
11                 {6, 7, 1, 4, 5},
12                 {5, 1, 1, 2, 4}};
13         System.out.println(pacificAtlantic(heights));
14     }
15 
16     public static List<List<Integer>> pacificAtlantic(int[][] heights) {
17         int[] X = {0, 0, 1, -1};
18         int[] Y = {1, -1, 0, 0};
19         int m = heights.length;
20         int n = heights[0].length;
21         List<List<Integer>> can_reach = new ArrayList<>();
22         boolean[][] can_reach_p = new boolean[m][n];
23         boolean[][] can_reach_a = new boolean[m][n];
24 
25         for (int i = 0; i < m; i++) {
26             DFS_pacificAtlantic(heights, can_reach_a, i, 0, X, Y); // 左边
27             DFS_pacificAtlantic(heights, can_reach_p, i, n - 1, X, Y); // 右边
28         }
29         for (int i = 0; i < n; i++) {
30             DFS_pacificAtlantic(heights, can_reach_a, 0, i, X, Y); // 上边
31             DFS_pacificAtlantic(heights, can_reach_p, m - 1, i, X, Y); // 下边
32         }
33         for (int i = 0; i < m; i++) {
34             for (int j = 0; j < n; j++) {
35                 if (can_reach_a[i][j] && can_reach_p[i][j]) {
36                     List<Integer> can = new ArrayList<>();
37                     can.add(i);
38                     can.add(j);
39                     can_reach.add(can);
40                 }
41             }
42         }
43         return can_reach;
44     }
45 
46     public static void DFS_pacificAtlantic(int[][] heights, boolean[][] can_reach, int r, int c, int[] X, int[] Y) {
47         if (can_reach[r][c]) {
48             return;
49         }
50         can_reach[r][c] = true;
51         for (int i = 0; i < 4; i++) {
52             int newX = r + X[i];
53             int newY = c + Y[i];
54             if (newX >= 0 && newY >= 0 && newX < heights.length && newY < heights[0].length && heights[r][c] <= heights[newX][newY]) {
55                 DFS_pacificAtlantic(heights, can_reach, newX, newY, X, Y);
56             }
57         }
58     }
59 }
View Code

二、回溯法

4、全排列

问题:

给定一个不含重复数字的数组 nums ,返回其所有可能的全排列 。你可以按任意顺序返回答案。

示例 1:

输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
示例 2:

输入:nums = [0,1]
输出:[[0,1],[1,0]]
示例 3:

输入:nums = [1]
输出:[[1]]

 1 package LeetCode.test5_sousuosuanfa;
 2 
 3 import java.util.ArrayList;
 4 import java.util.Collections;
 5 import java.util.List;
 6 
 7 /**
 8     思路:怎样输出所有的排列方式呢?对于每一个当前位置 i,可以将其于之后的任意位置交换,
 9     然后继续处理位置 i+1,直到处理到最后一位。为了防止每此遍历时都要新建一个子数组储
10     存位置 i 之前已经交换好的数字,可以利用回溯法,只对原数组进行修改,在递归完成后再
11     修改回来。
12  */
13 public class ques_46_全排列 {
14     public static void main(String[] args) {
15         int[] nums = {1, 2, 3};
16         System.out.println(permute(nums));
17     }
18 
19     public static List<List<Integer>> permute(int[] nums) {
20         List<Integer> perm = new ArrayList<>();
21         for (Integer num: nums) {
22             perm.add(num);
23         }
24         List<List<Integer>> res = new ArrayList<>();
25         backtracking(perm, 0, res);
26         return res;
27     }
28     /*
29     为什么在往结果集中添加排列结果的时候,需要进行new ArrayList添加??????
30     假设这段代码,「正确」res.add(new ArrayList<>(output)); 变为「错误」res.add(output);关键在于res存放的是list引用。
31     那么回溯过程中,将数字使用状态重置撤销的时候,会将list的元素移除掉,也会影响到res里面的list情况。
32     因为它们是同一个引用。全部为空,是因为回溯结束的同时,会将全部数字重置撤销,这样list里面的元素就会为空了,同样的,也会影响到res的存放情况。
33      */
34     public static void backtracking(List<Integer> perm, int first, List<List<Integer>> res) {
35         if (first == perm.size() - 1) {
36             res.add(new ArrayList<>(perm));
37             return;
38         }
39         for (int i = first; i < perm.size(); i++) {
40             Collections.swap(perm,i,first);  //修改当前节点状态
41             backtracking(perm,first+1,res); //递归子节点
42             Collections.swap(perm,i,first); //回改当前节点状态
43         }
44     }
45 }
View Code
 1 package LeetCode.test5_sousuosuanfa;
 2 
 3 import java.util.ArrayList;
 4 import java.util.List;
 5 
 6 public class ques_46_全排列 {
 7     public static void main(String[] args) {
 8         int[] nums = {1, 2, 3};
 9 //        int[] nums = {1, 1, 2};
10         System.out.println(permuteUnique(nums));
11     }
12 
13     public static List<List<Integer>> permuteUnique(int[] nums) {
14         List<Integer> perm = new ArrayList<>(); // 每种排列的容器
15         List<List<Integer>> res = new ArrayList<>();  // 所有排列的容器
16         boolean[] visited = new boolean[nums.length];
17         backtracking(visited, 0, nums, perm, res);
18         return res;
19     }
20 
21     public static void backtracking(boolean[] visited, int index, int[] nums, List<Integer> perm, List<List<Integer>> res) {
22         if (index == nums.length) {
23             res.add(new ArrayList<>(perm));
24             return;
25         }
26         for (int j = 0; j < nums.length; j++) {
27             if (visited[j]) {
28                 continue;
29             }
30             perm.add(nums[j]);
31             visited[j] = true;
32             backtracking(visited, index + 1, nums, perm, res);
33             visited[j] = false;
34             perm.remove(index);
35         }
36     }
37 }
View Code

5、组合

问题:

给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。

你可以按 任何顺序 返回答案。

示例 1:

输入:n = 4, k = 2
输出:
[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
]
示例 2:

输入:n = 1, k = 1
输出:[[1]]

 1 package LeetCode.test5_sousuosuanfa;
 2 
 3 import java.util.ArrayList;
 4 import java.util.Arrays;
 5 import java.util.List;
 6 
 7 /**
 8  思路:类似于排列问题,也可以进行回溯。排列回溯的是交换的位置,而组合回溯的是否把当前的数字加入结果中。
 9  */
10 public class ques_77_组合 {
11     public static void main(String[] args) {
12         int n = 4;
13         int k = 2;
14         System.out.println(combine(n, k));
15     }
16 
17     public static List<List<Integer>> combine(int n, int k) {
18         List<List<Integer>> res = new ArrayList<>();
19         Integer[] perm = new Integer[k];
20         int count = 0;
21         backtracking(res, n, k, count, 1, perm);
22         return res;
23     }
24 
25     public static void backtracking(List<List<Integer>> res, int n, int k, int count, int pos, Integer[] perm) {
26         if (count == k) {
27             res.add(new ArrayList<>(Arrays.asList(perm)));
28             return;
29         }
30         for (int i = pos; i <= n; i++) {
31             perm[count++] = i;
32             backtracking(res, n, k, count, i + 1, perm);
33             count--;
34         }
35     }
36 }
View Code

6、单词搜索

问题:

给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。

单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。

示例 1:

输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED"
输出:true
示例 2:

输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "SEE"
输出:true
示例 3:

输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCB"
输出:false

 1 package LeetCode.test5_sousuosuanfa;
 2 
 3 /**
 4  * 思路:不同于排列组合问题,本题采用的并不是修改输出方式,而是修改访问标记。在对任意
 5  * 位置进行深度优先搜索时,先标记当前位置为已访问,以避免重复遍历(如防止向右搜索后
 6  * 又向左返回);在所有的可能都搜索完成后,再回改当前位置为未访问,防止干扰其它位置搜索
 7  * 到当前位置。使用回溯法,可以只对一个二维的访问矩阵进行修改,而不用把每次的搜索状
 8  * 态作为一个新对象传入递归函数中。
 9  */
10 public class ques_79_单词搜索 {
11     public static void main(String[] args) {
12         char[][] board = {{'A', 'B', 'C', 'E'},
13                 {'S', 'F', 'C', 'S'},
14                 {'A', 'D', 'E', 'E'}};
15         String word = "ABCCED";
16         System.out.println(exist(board, word));
17     }
18 
19     public static boolean exist(char[][] board, String word) {
20         int m = board.length;
21         int n = board[0].length;
22         boolean[][] visited = new boolean[m][n];
23         for (int i = 0; i < m; i++) {
24             for (int j = 0; j < n; j++) {
25                 if (backtracking(i, j, board, word, visited, 0)){
26                     return true;
27                 }
28             }
29         }
30         return false;
31     }
32 
33     public static boolean backtracking(int i, int j, char[][] board, String word, boolean[][] visited, int pos) {
34         if (i < 0 || j < 0 || i >= board.length || j >= board[0].length) {
35             return false;
36         }
37         if (visited[i][j] || board[i][j] != word.charAt(pos)) {
38             return false;
39         }
40         if (pos == word.length() - 1) {
41             return true;
42         }
43         visited[i][j] = true;
44         boolean res = (backtracking(i + 1, j, board, word, visited,  pos + 1)||
45                         backtracking(i - 1, j, board, word, visited,  pos + 1)||
46                         backtracking(i, j + 1, board, word, visited,  pos + 1)||
47                         backtracking(i, j - 1, board, word, visited,  pos + 1));
48         visited[i][j] = false;
49         return res;
50     }
51 }
View Code

7、N皇后

问题:

n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。

给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。

每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 'Q' 和 '.' 分别代表了皇后和空位。

示例 1:

输入:n = 4
输出:[[".Q..","...Q","Q...","..Q."],["..Q.","Q...","...Q",".Q.."]]
解释:如上图所示,4 皇后问题存在两个不同的解法。
示例 2:

输入:n = 1
输出:[["Q"]]

 1 package LeetCode.test5_sousuosuanfa;
 2 
 3 import java.util.ArrayList;
 4 import java.util.Arrays;
 5 import java.util.List;
 6 
 7 public class ques_51_N皇后 {
 8     public static void main(String[] args) {
 9         int n = 4;
10         System.out.println(solveNQueens(n));
11     }
12 
13     public static List<List<String>> solveNQueens(int n) {
14         List<List<String>> ans = new ArrayList<>();
15         char[][] board = new char[n][n];
16         for (int i = 0; i < n; i++) {
17             Arrays.fill(board[i], '.');
18         }
19         //列数组长度为n
20         boolean[] column = new boolean[n];
21         //假设n=4:[0,1,2,3,4,5,6] 在同一45度线上的元素,row+col刚好都相等,数组长度为2*n-1  数组下标:row+col
22         boolean[] diag_45 = new boolean[2 * n - 1];
23         //假设n=4:[-3,-2,-1,0,1,2,3] 在同一45度线上的元素,row-col刚好都相等,数组长度为2*n-1  数组下标:n-1-(row-col)
24         boolean[] diag_135 = new boolean[2 * n - 1];
25         backtracking(ans, board, column, diag_45, diag_135, 0, n);
26         return ans;
27     }
28 
29     public static void backtracking(List<List<String>> ans, char[][] board, boolean[] column,
30                                     boolean[] diag_45, boolean[] diag_135, int row, int n) {
31         if (row == n) {
32             List<String> list = new ArrayList<>();
33             for (int i = 0; i < n; i++) {
34                 list.add(String.valueOf(board[i]));
35             }
36             ans.add(list);
37         }
38         for (int col = 0; col < n; col++) {
39             if (column[col] || diag_45[row + col] || diag_135[n - 1 - (row - col)]) {
40                 continue;
41             }
42             board[row][col] = 'Q'; // 修改当前节点状态
43             column[col] = diag_45[row + col] = diag_135[n - 1 - (row - col)] = true;
44             backtracking(ans, board, column, diag_45, diag_135, row + 1, n); // 递归子节点
45             board[row][col] = '.'; // 回改当前节点状态
46             column[col] = diag_45[row + col] = diag_135[n - 1 - (row - col)] = false;
47         }
48     }
49 }
View Code

三、广度优先搜索(BFS)

8、最短的桥

问题:

在给定的二维二进制数组 A 中,存在两座岛。(岛是由四面相连的 1 形成的一个最大组。)

现在,我们可以将 0 变为 1,以使两座岛连接起来,变成一座岛。

返回必须翻转的 0 的最小数目。(可以保证答案至少是 1 。)

示例 1:

输入:A = [[0,1],[1,0]]
输出:1
示例 2:

输入:A = [[0,1,0],[0,0,0],[0,0,1]]
输出:2
示例 3:

输入:A = [[1,1,1,1,1],[1,0,0,0,1],[1,0,1,0,1],[1,0,0,0,1],[1,1,1,1,1]]
输出:1

 1 package LeetCode.test5_sousuosuanfa;
 2 
 3 import java.util.*;
 4 
 5 /**
 6  * 最短!!!
 7  */
 8 public class ques_934_最短的桥 {
 9     public static void main(String[] args) {
10         int[][] grid = {{0, 1, 0},
11                         {0, 0, 0},
12                         {0, 0, 1}};
13 //        int[][] grid = {{1, 1, 1, 1, 1},
14 //                {1, 0, 0, 0, 1},
15 //                {1, 0, 1, 0, 1},
16 //                {1, 0, 0, 0, 1},
17 //                {1, 1, 1, 1, 1}};
18         System.out.println(shortestBridge(grid));
19     }
20 
21     public static int shortestBridge(int[][] grid) {
22         int[] X = {0, 0, 1, -1};
23         int[] Y = {1, -1, 0, 0};
24 
25         // (1) dfs搜索第一个岛屿,并把1全部赋值为2
26         boolean flag = false;  //保证只搜索第一个岛屿
27         for (int i = 0; i < grid.length; i++) {
28             if (flag) break;  //内层循环结束,外层循环也必须结束
29             for (int j = 0; j < grid[0].length; j++) {
30                 if (grid[i][j] == 1) {
31                     dfs(grid, i, j, X, Y);
32                     flag = true;
33                     break;
34                 }
35             }
36         }
37         // (2) bfs:首先将所有的2的节点放入队列,搜索第二个岛屿,并把过程中经过的0赋值为2
38         Queue<int[]> points = new LinkedList<>();
39         for (int i = 0; i < grid.length; i++) {
40             for (int j = 0; j < grid[0].length; j++) {
41                 if (grid[i][j] == 2) {
42                     points.offer(new int[]{i, j});
43                 }
44             }
45         }
46         int count = 0;
47         while (!points.isEmpty()) {
48             int size = points.size();
49             for (int j = 0; j < size; j++) {
50                 int[] top = points.poll();
51                 for (int i = 0; i < 4; i++) {
52                     assert top != null;
53                     int newX = top[0] + X[i];
54                     int newY = top[1] + Y[i];
55                     if (newX < 0 || newX >= grid.length || newY < 0 || newY >= grid[0].length) {
56                         continue;
57                     }
58                     if (grid[newX][newY] == 2) {
59                         continue;
60                     }
61                     if (grid[newX][newY] == 1) {
62                         return count;
63                     }
64                     grid[newX][newY] = 2;
65                     points.offer(new int[]{newX, newY});
66                 }
67             }
68             count++;
69         }
70         return count;
71     }
72 
73     public static void dfs(int[][] grid, int i, int j, int[] X, int[] Y) {
74         if (grid[i][j] == 1) {
75             grid[i][j] = 2;
76         }
77         for (int k = 0; k < 4; k++) {
78             int newX = i + X[k];
79             int newY = j + Y[k];
80             if (newX < 0 || newX >= grid.length || newY < 0 || newY >= grid[0].length) {
81                 continue;
82             }
83             if (grid[newX][newY] == 0 || grid[newX][newY] == 2) {
84                 continue;
85             }
86             dfs(grid, newX, newY, X, Y);
87         }
88     }
89 }
View Code

9、单词接龙II

问题:(https://blog.csdn.net/zzzzz_zzz___rss/article/details/121286731)

按字典 wordList 完成从单词 beginWord 到单词 endWord 转化,一个表示此过程的 转换序列 是形式上像 beginWord -> s1 -> s2 -> ... -> sk 这样的单词序列,并满足:

每对相邻的单词之间仅有单个字母不同。
转换过程中的每个单词 si(1 <= i <= k)必须是字典 wordList 中的单词。注意,beginWord 不必是字典 wordList 中的单词。
sk == endWord
给你两个单词 beginWord 和 endWord ,以及一个字典 wordList 。请你找出并返回所有从 beginWord 到 endWord 的 最短转换序列 ,如果不存在这样的转换序列,返回一个空列表。

每个序列都应该以单词列表 [beginWord, s1, s2, ..., sk] 的形式返回。

示例 1:

输入:beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log","cog"]
输出:[["hit","hot","dot","dog","cog"],["hit","hot","lot","log","cog"]]
解释:存在 2 种最短的转换序列:
"hit" -> "hot" -> "dot" -> "dog" -> "cog"
"hit" -> "hot" -> "lot" -> "log" -> "cog"
示例 2:

输入:beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log"]
输出:[]
解释:endWord "cog" 不在字典 wordList 中,所以不存在符合要求的转换序列。

思路:

 (1)使用双向BFS的方法,每次从队列长度小的一端开始搜索。

(2)在对某一层BFS开始之前,先将队列中的全部元素进行标记。

 (3)然后开始BFS,如果遍历到的节点未被标记,那么将该节点放入队列中,如果该节点存在于另一个队列中,说明已经找到最短的路径了。

(4)此时将该层节点遍历结束后,方可进行回溯记录路径。

(5)此外,需要在遍历节点的时候,通过判断这次遍历是从后向前还是从前向后,以确定构建的图的起始节点和结束节点。

  1 package LeetCode.test5_sousuosuanfa;
  2 
  3 import java.util.*;
  4 
  5 /**
  6  * 最短!!!
  7  */
  8 public class ques_126_单词接龙II {
  9     public static void main(String[] args) {
 10         String beginWord = "hit";
 11         String endWord = "cog";
 12         String[] word = {"hot", "dot", "dog", "lot", "log", "cog"};
 13         List<String> wordList = new ArrayList<>(Arrays.asList(word));
 14         System.out.println((findLadders(beginWord, endWord, wordList)));
 15     }
 16 
 17     public static List<List<String>> findLadders(String beginWord, String endWord, List<String> wordList) {
 18         wordList.add(beginWord);
 19         // 构建一个标记集合,开始每个word都在集合中。后边每次将元素放到队列中,便将该元素从集合中删除.
 20         Set<String> wordListSet = new HashSet<>(wordList);
 21         if (!wordList.contains(endWord)) {
 22             return new ArrayList<>();
 23         }
 24         // 标记是不是已经找到了最短的路径,并用res来记录这些路径
 25         boolean found = false;
 26         List<List<String>> res = new ArrayList<>();
 27         // 构建map, 用来记录单词之间的关系
 28         Map<String, Set<String>> map = new HashMap<>();
 29         // 使用双向的BFS来检索最短的变换路径
 30         Queue<String> queueBegin = new LinkedList<>();
 31         Queue<String> queueEnd = new LinkedList<>();
 32         queueBegin.add(beginWord);
 33         queueEnd.add(endWord);
 34         // 每次只对比较短的BFS进行搜索,使用reverse标记两个方向的队列是不是交换过。
 35         boolean reverse = false;
 36         while (!queueBegin.isEmpty() && !queueEnd.isEmpty()) {
 37             if (queueBegin.size() > queueEnd.size()) {
 38                 Queue<String> queue = queueBegin;
 39                 queueBegin = queueEnd;
 40                 queueEnd = queue;
 41                 // 标记是不是转换过,如果转换过的话,需要在构建图的时候将两个节点逆转
 42                 reverse = !reverse;
 43             }
 44             // 将上一层访问过的节点从集合中删除
 45             wordListSet.removeAll(queueBegin);
 46             // 根据queueEnd创建一个集合,用来判断遍历到的单词是不是出现在另一端遍历的队列中
 47             Set<String> queueEndToSet = new HashSet<>(queueEnd);
 48             int count = queueBegin.size();
 49             while (count-- > 0) {
 50                 String top = queueBegin.poll();
 51                 assert top != null;
 52                 char[] tops = top.toCharArray();
 53                 for (int i = 0; i < tops.length; i++) {
 54                     char c = tops[i];
 55                     for (char j = 'a'; j <= 'z'; j++) {
 56                         tops[i] = j;
 57                         String newStr = new String(tops);
 58                         // 如果当前元素存在于另一个队列中,说明找到了最短的路径
 59                         // 只需要将当前的两个队列遍历一遍,找到全部的结果集,而不需要再次向下遍历。
 60                         if (queueEndToSet.contains(newStr)) {
 61                             found = true;
 62                         }
 63                         // 如果未被访问过,那么将该元素放到queueBegin中。
 64                         if (wordListSet.contains(newStr)) {
 65                             queueBegin.add(newStr);
 66                         }
 67                         // 通过判断是不是reverse过,来构建节点关系图(保证其正向)
 68                         if (wordListSet.contains(newStr) || queueEndToSet.contains(newStr)) {
 69                             Set<String> subList;
 70                             if (reverse) {
 71                                 subList = map.get(newStr);
 72                                 if (subList == null) {
 73                                     subList = new HashSet<>();
 74                                 }
 75                                 subList.add(top);
 76                                 // {dot=[dog], lot=[log], hit=[hot], hot=[lot, dot], dog=[cog], log=[cog]}
 77                                 map.put(newStr, subList);
 78                             } else {
 79                                 subList = map.get(top);
 80                                 if (subList == null) {
 81                                     subList = new HashSet<>();
 82                                 }
 83                                 subList.add(newStr);
 84                                 // {hit=[hot], hot=[lot, dot]}
 85                                 map.put(top, subList);
 86                             }
 87                         }
 88                     }
 89                     tops[i] = c;
 90                 }
 91             }
 92             // 将其放到最后进行遍历,原因在于:
 93             // 在构建图的时候,需要考虑到,此时两个遍历方向的队列已经到了中间层。如果得到结果就直接返回的话,肯定会导致缺少路径。
 94             // 因此在while(count--)之后再回溯记录路径,便会找到全部的路径
 95             if (found){
 96                 List<String> path = new ArrayList<>();
 97                 path.add(beginWord);
 98                 dfs(map, path, res, beginWord, endWord);
 99                 return res;
100             }
101         }
102         return res;
103     }
104 
105     // 回溯法记录路径
106     public static void dfs(Map<String, Set<String>> map, List<String> path, List<List<String>> res, String beginWord, String endWord) {
107         if (beginWord.equals(endWord)){
108             res.add(new ArrayList<>(path));
109             return;
110         }
111         if (map.get(beginWord)!=null){
112             for (String str:map.get(beginWord)) {
113                 path.add(str);
114                 dfs(map, path, res, str, endWord);
115                 path.remove(str);
116             }
117         }
118     }
119 }
View Code

10、单词接龙

问题:(https://blog.csdn.net/zzzzz_zzz___rss/article/details/121256294)

字典 wordList 中从单词 beginWord 和 endWord 的 转换序列 是一个按下述规格形成的序列:

序列中第一个单词是 beginWord 。
序列中最后一个单词是 endWord 。
每次转换只能改变一个字母。
转换过程中的中间单词必须是字典 wordList 中的单词。
给你两个单词 beginWord 和 endWord 和一个字典 wordList ,找到从 beginWord 到 endWord 的 最短转换序列 中的 单词数目 。如果不存在这样的转换序列,返回 0。

示例 1:

输入:beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log","cog"]
输出:5
解释:一个最短转换序列是 "hit" -> "hot" -> "dot" -> "dog" -> "cog", 返回它的长度 5。
示例 2:

输入:beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log"]
输出:0
解释:endWord "cog" 不在字典中,所以无法进行转换。

方法1:单向BFS

 1 package LeetCode.test5_sousuosuanfa;
 2 
 3 import java.util.*;
 4 
 5 /**
 6  * 最短!!!
 7  * 方法1:单向BFS
 8  */
 9 public class ques_127_单词接龙 {
10     public static void main(String[] args) {
11         String beginWord = "hit";
12         String endWord = "cog";
13         String[] word = {"hot","dot","dog","lot","log","cog"};
14         List<String> wordList = new ArrayList<>(Arrays.asList(word));
15         System.out.println((findLadders(beginWord, endWord, wordList)));
16     }
17 
18     public static int  findLadders(String beginWord, String endWord, List<String> wordList) {
19         // bfs的深度
20         int level = 0;
21         wordList.add(beginWord);
22         if (!wordList.contains(endWord)) {
23             return 0;
24         }
25         // 构建map, 用来记录单词之间的关系
26         Map<String, Set<String>> wordMap = new HashMap<>();
27         for (String s: wordList) {
28             Set<String> set = new HashSet<>();
29             for (String ss : wordList) {
30                 if (checkDiff(ss, s)) {  //检查ss和s是不是只有一个字符不相同
31                     set.add(ss);
32                 }
33             }
34             wordMap.put(s, set);
35         }
36         // 标记单词是不是已经使用过
37         Set<String> map = new HashSet<>();
38         Set<String> tmpMap = new HashSet<>();
39 
40         // 定义一个Queue来实现bfs
41         Queue<String> queue = new ArrayDeque<>();
42         queue.add(beginWord);
43         while (!queue.isEmpty()) {
44             int count = queue.size();
45             level++;
46             // 将上一层bfs使用过的单词,在该层统一进行标记
47             map.addAll(tmpMap);
48             tmpMap.clear();
49             while (count-- > 0) {
50                 // 拿出queue中的元素,并在list中查找与其仅有一个字符不同的单词
51                 String key = queue.remove();
52                 // 将key临时记录,并在当前循环结束之后放入map中记录,之后不再访问之
53                 tmpMap.add(key);
54                 // 在Map中获取与当前单词key直接相连的单词
55                 Set<String> list = wordMap.get(key);
56                 for (String s : list) {
57                     // 找到list中未曾访问过的某个元素
58                     if (!map.contains(s)) {
59                         queue.add(s);
60                         if (s.equals(endWord)) {
61                             return level + 1;
62                         }
63                     }
64                 }
65             }
66         }
67         return 0;
68     }
69 
70     public static boolean checkDiff(String key, String str) {
71         if (key == null || str == null) {
72             return false;
73         }
74         int keyLen = key.length();
75         int strLen = str.length();
76         if (keyLen != strLen) {
77             return false;
78         }
79         int count = 0;
80         for (int i = 0; i < keyLen; i++) {
81             if (key.charAt(i) != str.charAt(i)) {
82                 count++;
83                 if (count > 1) {
84                     return false;
85                 }
86             }
87         }
88         return count == 1;
89     }
90 }
View Code

方法2:双向BFS

 1 package LeetCode.test5_sousuosuanfa;
 2 
 3 import java.util.*;
 4 
 5 /**
 6  * 最短!!!
 7  * 方法2:双向BFS
 8  */
 9 public class ques_127_单词接龙_2 {
10     public static void main(String[] args) {
11         String beginWord = "hit";
12         String endWord = "cog";
13         String[] word = {"hot", "dot", "dog", "lot", "log", "cog"};
14         List<String> wordList = new ArrayList<>(Arrays.asList(word));
15         System.out.println((findLadders(beginWord, endWord, wordList)));
16     }
17 
18     public static int findLadders(String beginWord, String endWord, List<String> wordList) {
19         // 两个bfs的深度
20         int level = 0;
21         wordList.add(beginWord);
22         // 构建一个标记集合,开始每个word都在集合中。后边每次将元素放到队列中,便将该元素从集合中删除.
23         Set<String> wordListSet = new HashSet<>(wordList);
24         if (!wordList.contains(endWord)) {
25             return 0;
26         }
27 
28         // 标记单词是不是已经使用过 - 从begin处向后
29         Set<String> usedBegin = new HashSet<>();
30         // 标记单词是不是已经使用过 - 从end处向前
31         Set<String> usedEnd = new HashSet<>();
32 
33         // 定义一个Queue来实现bfs
34         Queue<String> queueBegin = new ArrayDeque<>();
35         Queue<String> queueEnd = new ArrayDeque<>();
36         queueEnd.add(endWord);
37         queueBegin.add(beginWord);
38 
39         usedBegin.add(beginWord);
40         usedEnd.add(endWord);
41 
42         while (!queueBegin.isEmpty() && !queueEnd.isEmpty()) {
43             level++;
44             if (queueBegin.size() > queueEnd.size()) {
45                 // 交换
46                 Queue<String> queue = queueBegin;
47                 queueBegin = queueEnd;
48                 queueEnd = queue;
49                 // 交换
50                 Set<String> set = usedBegin;
51                 usedBegin = usedEnd;
52                 usedEnd = set;
53             }
54             int count = queueBegin.size();
55             while (count-- > 0) {
56                 String top = queueBegin.poll();
57                 assert top != null;
58                 char[] tops = top.toCharArray();
59                 for (int i = 0; i < tops.length; i++) {
60                     char c = tops[i];
61                     for (char j = 'a'; j <= 'z'; j++) {
62                         tops[i] = j;
63                         String newStr = new String(tops);
64                         if (usedBegin.contains(newStr)) {
65                             continue;
66                         }
67                         if (usedEnd.contains(newStr)) {
68                             return level + 1;
69                         }
70                         if (wordListSet.contains(newStr)) {
71                             queueBegin.add(newStr);
72                             usedBegin.add(newStr);
73                         }
74                     }
75                     tops[i] = c;
76                 }
77             }
78         }
79         return 0;
80     }
81 }
View Code
 1 package LeetCode.test5_sousuosuanfa;
 2 
 3 import java.util.*;
 4 
 5 /**
 6  * 最短!!!
 7  * 方法2:双向BFS
 8  */
 9 public class ques_127_单词接龙_3 {
10     public static void main(String[] args) {
11         String beginWord = "hit";
12         String endWord = "cog";
13         String[] word = {"hot", "dot", "dog", "lot", "log", "cog"};
14         List<String> wordList = new ArrayList<>(Arrays.asList(word));
15         System.out.println((findLadders(beginWord, endWord, wordList)));
16     }
17 
18     public static int findLadders(String beginWord, String endWord, List<String> wordList) {
19         wordList.add(beginWord);
20         // 构建一个标记集合,开始每个word都在集合中。后边每次将元素放到队列中,便将该元素从集合中删除.
21         Set<String> wordListSet = new HashSet<>(wordList);
22         if (!wordList.contains(endWord)) {
23             return 0;
24         }
25         // 两个bfs的深度
26         int level = 0;
27         // 构建map, 用来记录单词之间的关系
28         Map<String, Set<String>> map = new HashMap<>();
29         // 使用双向的BFS来检索最短的变换路径
30         Queue<String> queueBegin = new LinkedList<>();
31         Queue<String> queueEnd = new LinkedList<>();
32         queueBegin.add(beginWord);
33         queueEnd.add(endWord);
34         // 每次只对比较短的BFS进行搜索,使用reverse标记两个方向的队列是不是交换过。
35         boolean reverse = false;
36         while (!queueBegin.isEmpty() && !queueEnd.isEmpty()) {
37             level++;
38             if (queueBegin.size() > queueEnd.size()) {
39                 Queue<String> queue = queueBegin;
40                 queueBegin = queueEnd;
41                 queueEnd = queue;
42                 // 标记是不是转换过,如果转换过的话,需要在构建图的时候将两个节点逆转
43                 reverse = !reverse;
44             }
45             // 将上一层访问过的节点从集合中删除
46             wordListSet.removeAll(queueBegin);
47             // 根据queueEnd创建一个集合,用来判断遍历到的单词是不是出现在另一端遍历的队列中
48             Set<String> queueEndToSet = new HashSet<>(queueEnd);
49             int count = queueBegin.size();
50             while (count-- > 0) {
51                 String top = queueBegin.poll();
52                 assert top != null;
53                 char[] tops = top.toCharArray();
54                 for (int i = 0; i < tops.length; i++) {
55                     char c = tops[i];
56                     for (char j = 'a'; j <= 'z'; j++) {
57                         tops[i] = j;
58                         String newStr = new String(tops);
59                         // 如果当前元素存在于另一个队列中,说明找到了最短的路径
60                         if (queueEndToSet.contains(newStr)) {
61                             return level + 1;
62                         }
63                         // 如果未被访问过,那么将该元素放到queueBegin中。
64                         if (wordListSet.contains(newStr)) {
65                             queueBegin.add(newStr);
66                         }
67                         // 通过判断是不是reverse过,来构建节点关系图(保证其正向)
68                         if (wordListSet.contains(newStr) || queueEndToSet.contains(newStr)) {
69                             Set<String> subList;
70                             if (reverse) {
71                                 subList = map.get(newStr);
72                                 if (subList == null) {
73                                     subList = new HashSet<>();
74                                 }
75                                 subList.add(top);
76                                 // {dot=[dog], lot=[log], hit=[hot], hot=[lot, dot], dog=[cog], log=[cog]}
77                                 map.put(newStr, subList);
78                             } else {
79                                 subList = map.get(top);
80                                 if (subList == null) {
81                                     subList = new HashSet<>();
82                                 }
83                                 subList.add(newStr);
84                                 // {hit=[hot], hot=[lot, dot]}
85                                 map.put(top, subList);
86                             }
87                         }
88                     }
89                     tops[i] = c;
90                 }
91             }
92         }
93         return 0;
94     }
95 }
View Code

练习(6道)

1、被围绕的区域

问题:

 给你一个 m x n 的矩阵 board ,由若干字符 'X' 和 'O' ,找到所有被 'X' 围绕的区域,并将这些区域里所有的 'O' 用 'X' 填充。

示例 1:

输入:board = [["X","X","X","X"],["X","O","O","X"],["X","X","O","X"],["X","O","X","X"]]
输出:[["X","X","X","X"],["X","X","X","X"],["X","X","X","X"],["X","O","X","X"]]
解释:被围绕的区间不会存在于边界上,换句话说,任何边界上的 'O' 都不会被填充为 'X'。 任何不在边界上,或不与边界上的 'O' 相连的 'O' 最终都会被填充为 'X'。如果两个元素在水平或垂直方向相邻,则称它们是“相连”的。
示例 2:

输入:board = [["X"]]
输出:[["X"]]

 1 package LeetCode.test5_sousuosuanfa;
 2 
 3 import java.util.Arrays;
 4 
 5 public class ques_130_被围绕的区域 {
 6     public static void main(String[] args) {
 7         char[][] board = {{'X', 'X', 'X', 'X'},
 8                 {'X', 'O', 'O', 'X'},
 9                 {'X', 'X', 'O', 'X'},
10                 {'X', 'O', 'X', 'X'}};
11 //        char[][] board = {{'O', 'O', 'O'},
12 //                {'O', 'O', 'O'},
13 //                {'O', 'O', 'O'}};
14         solve(board);
15         System.out.println(Arrays.deepToString(board));
16     }
17 
18     public static void solve(char[][] board) {
19         int m = board.length;
20         int n = board[0].length;
21         for (int i = 0; i < m; i++) {
22             dfs(board, i, 0);
23             dfs(board, i, n - 1);
24         }
25         for (int i = 0; i < n; i++) {
26             dfs(board, 0, i);
27             dfs(board, m - 1, i);
28         }
29         for (int i = 0; i < m; i++) {
30             for (int j = 0; j < n; j++) {
31                 if (board[i][j] == 'C') {
32                     board[i][j] = 'O';
33                 } else if (board[i][j] == 'O') {
34                     board[i][j] = 'X';
35                 }
36             }
37         }
38     }
39 
40     public static void dfs(char[][] board, int i, int j) {
41         if (i < 0 || i >= board.length || j < 0 || j >= board[0].length || board[i][j] != 'O') {
42             return;
43         }
44         board[i][j] = 'C';
45         dfs(board, i, j + 1);
46         dfs(board, i, j - 1);
47         dfs(board, i + 1, j);
48         dfs(board, i - 1, j);
49     }
50 }
View Code

2、二叉树的所有路径

问题:

给你一个二叉树的根节点 root ,按 任意顺序 ,返回所有从根节点到叶子节点的路径。叶子节点是指没有子节点的节点。

示例 1:

输入:root = [1,2,3,null,5]
输出:["1->2->5","1->3"]
示例 2:

输入:root = [1]
输出:["1"]

 1 package LeetCode.test5_sousuosuanfa;
 2 
 3 import java.util.ArrayList;
 4 import java.util.List;
 5 
 6 public class ques_257_二叉树的所有路径 {
 7     public static void main(String[] args) {
 8 //        String[] root = {"1", "2", "3", null, "5"};
 9         String[] root = {"1"};
10         int index = 0;
11         TreeNode bt = createBTree(root, index);
12         System.out.println(binaryTreePaths(bt));
13     }
14 
15     public static class TreeNode {
16         int val;
17         TreeNode left;
18         TreeNode right;
19 
20         TreeNode() {
21         }
22 
23         TreeNode(int val) {
24             this.val = val;
25         }
26 
27         TreeNode(int val, TreeNode left, TreeNode right) {
28             this.val = val;
29             this.left = left;
30             this.right = right;
31         }
32     }
33 
34 
35     public static List<String> binaryTreePaths(TreeNode root) {
36         List<String> paths = new ArrayList<>();
37         String s = "";
38         getPath(root, s, paths);
39         return paths;
40     }
41 
42     public static void getPath(TreeNode root, String s, List<String> paths) {
43         if (root != null) {
44             StringBuilder sb = new StringBuilder(s);
45             sb.append(root.val);
46             if (root.left == null && root.right == null) {
47                 paths.add(sb.toString());
48             } else {
49                 sb.append("->");
50                 getPath(root.left, sb.toString(), paths);
51                 getPath(root.right, sb.toString(), paths);
52             }
53         }
54     }
55 
56     public static TreeNode createBTree(String[] root, int index) {
57         TreeNode rootNode;
58         if (index >= root.length || root[index] == null) {
59             return null;
60         }
61         rootNode = new TreeNode(Integer.parseInt(root[index]));
62         rootNode.left = createBTree(root, 2 * index + 1);
63         rootNode.right = createBTree(root, 2 * index + 2);
64         return rootNode;
65     }
66 }
View Code

3、全排列II

问题:

给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。

示例 1:

输入:nums = [1,1,2]
输出:
[[1,1,2],
[1,2,1],
[2,1,1]]
示例 2:

输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

 1 package LeetCode.test5_sousuosuanfa;
 2 
 3 import java.util.ArrayList;
 4 import java.util.Arrays;
 5 import java.util.List;
 6 
 7 public class ques_47_全排列II {
 8     public static void main(String[] args) {
 9         int[] nums = {1, 1, 2};
10         System.out.println(permuteUnique(nums));
11     }
12 
13     public static List<List<Integer>> permuteUnique(int[] nums) {
14         List<Integer> perm = new ArrayList<>(); // 每种排列的容器
15         List<List<Integer>> res = new ArrayList<>();  // 所有排列的容器
16         boolean[] visited = new boolean[nums.length];
17         Arrays.sort(nums);
18         backtracking(visited, 0, nums, perm, res);
19         return res;
20     }
21 
22     public static void backtracking(boolean[] visited, int index, int[] nums, List<Integer> perm, List<List<Integer>> res) {
23         if (index == nums.length) {
24             res.add(new ArrayList<>(perm));
25             return;
26         }
27         for (int j = 0; j < nums.length; j++) {
28             // 重复问题:保证在填第index个数的时候重复数字只会被填入一次即可。
29             // 选择对原数组排序,保证相同的数字都相邻,然后每次填入的数一定是这个数所在重复数集合中(从左往右第一个未被填过的数字)
30             // eg({1, 1, 2}): index=0时,填第一个1,visited[0]=true
31             //                回退回来(如何避免index=0时填入第二个1) 此时visited[0]=false
32             //                保证nums[1] == nums[0]且visited[0]=false即可
33             if (visited[j] || (j > 0 && nums[j] == nums[j - 1] && !visited[j - 1])) {
34                 continue;
35             }
36             perm.add(nums[j]);
37             visited[j] = true;
38             backtracking(visited, index + 1, nums, perm, res);
39             visited[j] = false;
40             perm.remove(index);
41         }
42     }
43 }
View Code

补:不包含重复数字

 1 class Solution {
 2     public List<List<Integer>> permute(int[] nums) {
 3         List<List<Integer>> result = new ArrayList<>();
 4         List<Integer> res = new ArrayList<>();
 5         boolean[] flag = new boolean[nums.length]; 
 6         helper(nums,0,flag, result,res); 
 7         return result;
 8     }
 9 
10     public void helper(int[] nums,int index,boolean[] flag,List<List<Integer>> result,List<Integer> res){
11         if(index == nums.length){
12             result.add(new ArrayList<>(res));
13             return;
14         }
15         for(int i=0;i<nums.length;i++){
16             if(!flag[i]){
17                 res.add(nums[i]);
18                 flag[i]=true;
19                 helper(nums,index+1,flag,result,res);
20                 flag[i]=false;
21                 res.remove(index);
22             }
23         }
24     }
25 }
View Code

4、组合总和II

问题:

给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的每个数字在每个组合中只能使用一次。

注意:解集不能包含重复的组合。 

示例 1:

输入: candidates = [10,1,2,7,6,1,5], target = 8,
输出:
[
[1,1,6],
[1,2,5],
[1,7],
[2,6]
]
示例 2:

输入: candidates = [2,5,2,1,2], target = 5,
输出:
[
[1,2,2],
[5]
]

 1 package LeetCode.test5_sousuosuanfa;
 2 
 3 import java.util.ArrayList;
 4 import java.util.Arrays;
 5 import java.util.List;
 6 
 7 public class ques_40_组合总和II {
 8     public static void main(String[] args) {
 9         int[] candidates = {10, 1, 2, 7, 6, 1, 5};
10         int target = 8;
11         System.out.println(combinationSum2(candidates, target));
12     }
13 
14     public static List<List<Integer>> combinationSum2(int[] candidates, int target) {
15         Arrays.sort(candidates);
16         List<List<Integer>> res = new ArrayList<>();
17         List<Integer> perm = new ArrayList<>();
18         backtrack(candidates, 0, target, perm, res);
19         return res;
20     }
21 
22     public static void backtrack(int[] candidates, int begin, int target, List<Integer> perm, List<List<Integer>> res) {
23         int sum = perm.stream().reduce(0, Integer::sum);
24         if (sum == target) {
25             res.add(new ArrayList<>(perm));
26             return;
27         }
28         for (int i = begin; i < candidates.length; i++) {
29             if (i > begin && candidates[i] == candidates[i - 1]) {  // 避免重复
30                 continue;
31             }
32             if (sum < target) {
33                 perm.add(candidates[i]);
34                 backtrack(candidates, i + 1, target, perm, res);
35                 perm.remove(perm.size() - 1);
36             }else {
37                 break;
38             }
39         }
40     }
41 }
View Code

5、解数独

问题:

编写一个程序,通过填充空格来解决数独问题。

数独的解法需 遵循如下规则:

数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图)
数独部分空格内已填入了数字,空白格用 '.' 表示。

示例:

输入:board = [["5","3",".",".","7",".",".",".","."],["6",".",".","1","9","5",".",".","."],[".","9","8",".",".",".",".","6","."],["8",".",".",".","6",".",".",".","3"],["4",".",".","8",".","3",".",".","1"],["7",".",".",".","2",".",".",".","6"],[".","6",".",".",".",".","2","8","."],[".",".",".","4","1","9",".",".","5"],[".",".",".",".","8",".",".","7","9"]]
输出:[["5","3","4","6","7","8","9","1","2"],["6","7","2","1","9","5","3","4","8"],["1","9","8","3","4","2","5","6","7"],["8","5","9","7","6","1","4","2","3"],["4","2","6","8","5","3","7","9","1"],["7","1","3","9","2","4","8","5","6"],["9","6","1","5","3","7","2","8","4"],["2","8","7","4","1","9","6","3","5"],["3","4","5","2","8","6","1","7","9"]]
解释:输入的数独如上图所示,唯一有效的解决方案如下所示:

 

 1 package LeetCode.test5_sousuosuanfa;
 2 
 3 import java.util.ArrayList;
 4 import java.util.Arrays;
 5 import java.util.List;
 6 
 7 /**
 8  * 十分经典的数独题,可以利用回溯法求解。事实上对于数独类型的题,
 9  * 有很多进阶的搜索方法和剪枝策略可以提高速度,如启发式搜索。
10  */
11 public class ques_37_解数独 {
12     private boolean valid = false;
13     // row[2][3]=True表示数字4在第2行已经出现过,当遍历到第2行的空白格时,就不能填入数字4。
14     private boolean[][] row = new boolean[9][9];  //
15     private boolean[][] col = new boolean[9][9];
16     private boolean[][][] block = new boolean[3][3][9];
17     // 存储空格所在位置
18     private List<int[]> spaces = new ArrayList<>();
19 
20     public void solveSudoku(char[][] board) {
21 
22         for (int i = 0; i < 9; i++) {
23             for (int j = 0; j < 9; j++) {
24                 if (board[i][j] == '.') {
25                     spaces.add(new int[]{i, j});
26                 } else {
27                     int index = board[i][j] - '0' - 1;
28                     row[i][index] = col[j][index] = block[i / 3][j / 3][index] = true;
29                 }
30             }
31         }
32         backtracking(board, 0);
33     }
34 
35     public void backtracking(char[][] board, int pos) {
36         if (pos == spaces.size()) {
37             valid = true;
38             return;
39         }
40         int[] res = spaces.get(pos);
41         int i = res[0];
42         int j = res[1];
43         for (int index = 0; index < 9 && !valid; index++) {
44             if (!row[i][index] && !col[j][index] && !block[i / 3][j / 3][index]) {
45                 row[i][index] = col[j][index] = block[i / 3][j / 3][index] = true;
46                 board[i][j] = (char) (index + '0' + 1);
47                 backtracking(board, pos + 1);
48                 row[i][index] = col[j][index] = block[i / 3][j / 3][index] = false;
49             }
50         }
51     }
52 
53     public static void main(String[] args) {
54         char[][] board = {{'5', '3', '.', '.', '7', '.', '.', '.', '.'},
55                 {'6', '.', '.', '1', '9', '5', '.', '.', '.'},
56                 {'.', '9', '8', '.', '.', '.', '.', '6', '.'},
57                 {'8', '.', '.', '.', '6', '.', '.', '.', '3'},
58                 {'4', '.', '.', '8', '.', '3', '.', '.', '1'},
59                 {'7', '.', '.', '.', '2', '.', '.', '.', '6'},
60                 {'.', '6', '.', '.', '.', '.', '2', '8', '.'},
61                 {'.', '.', '.', '4', '1', '9', '.', '.', '5'},
62                 {'.', '.', '.', '.', '8', '.', '.', '7', '9'}};
63         ques_37_解数独 s = new ques_37_解数独();
64         s.solveSudoku(board);
65         System.out.println(Arrays.deepToString(board));
66     }
67 }
View Code

6、最小高度树

问题:

树是一个无向图,其中任何两个顶点只通过一条路径连接。 换句话说,一个任何没有简单环路的连通图都是一棵树。

给你一棵包含 n 个节点的树,标记为 0 到 n - 1 。给定数字 n 和一个有 n - 1 条无向边的 edges 列表(每一个边都是一对标签),其中 edges[i] = [ai, bi] 表示树中节点 ai 和 bi 之间存在一条无向边。

可选择树中任何一个节点作为根。当选择节点 x 作为根节点时,设结果树的高度为 h 。在所有可能的树中,具有最小高度的树(即,min(h))被称为 最小高度树 。

请你找到所有的 最小高度树 并按 任意顺序 返回它们的根节点标签列表。

树的 高度 是指根节点和叶子节点之间最长向下路径上边的数量。

示例 1:

输入:n = 4, edges = [[1,0],[1,2],[1,3]]
输出:[1]
解释:如图所示,当根是标签为 1 的节点时,树的高度是 1 ,这是唯一的最小高度树。
示例 2:

输入:n = 6, edges = [[3,0],[3,1],[3,2],[3,4],[5,4]]
输出:[3,4]
示例 3:

输入:n = 1, edges = []
输出:[0]
示例 4:

输入:n = 2, edges = [[0,1]]
输出:[0,1]

 1 package LeetCode.test5_sousuosuanfa;
 2 
 3 import java.util.*;
 4 
 5 /**
 6  * 思路:从边缘开始,先找到所有出度为1的节点,然后把所有出度为1的节点进队列,然后不断地bfs,
 7  * 最后找到的就是两边同时向中间靠近的节点,那么这个中间节点就相当于把整个距离二分了,
 8  * 那么它当然就是到两边距离最小的点啦,也就是到其他叶子节点最近的节点了。
 9  * 出现的问题:超出内存限制
10  */
11 public class ques_310_最小高度树 {
12     public static void main(String[] args) {
13         int[][] edges = {{3, 0}, {3, 1}, {3, 2}, {3, 4}, {5, 4}};
14         int n = 6;
15         System.out.println(findMinHeightTrees(n, edges));
16     }
17 
18     public static List<Integer> findMinHeightTrees(int n, int[][] edges) {
19         List<Integer> res = new ArrayList<>();
20         // 如果只有一个节点,那么他就是最小高度树
21         if (n == 1) {
22             res.add(0);
23             return res;
24         }
25         boolean[][] graph = new boolean[n][n];
26         int[] degree = new int[n];
27         for (int[] edge : edges) {
28             graph[edge[0]][edge[1]] = true;
29             graph[edge[1]][edge[0]] = true;
30             degree[edge[0]]++;
31             degree[edge[1]]++;
32         }
33         Queue<Integer> queue = new LinkedList<>();
34         for (int i = 0; i < n; i++) {  // 把所有出度为1的节点,也就是叶子节点入队
35             if (degree[i] == 1) {
36                 queue.offer(i);
37             }
38         }
39         while (!queue.isEmpty()) {
40             // 每层循环都要new一个新的结果集合,这样最后保存的就是最终的最小高度树了
41             res = new ArrayList<>();
42             int size = queue.size();
43             for (int i = 0; i < size; i++) {  // eg:假设最后只剩1或者2个节点
44                 int top = queue.poll();
45                 res.add(top);
46                 //经典bfs,把当前节点的相邻接点都拿出来
47                 List<Integer> neighbors = new ArrayList<>();
48                 for (int j = 0; j < graph.length; j++) {
49                     if (graph[j][top]) {
50                         neighbors.add(j);
51                     }
52                 }
53                 //把它们的出度都减1(当前节点已经不存在了),而它的相邻节点们就有可能变成叶子节点
54                 for (Integer neighbor : neighbors) {
55                     degree[neighbor]--;
56                     if (degree[neighbor] == 1) {
57                         queue.offer(neighbor);
58                     }
59                 }
60             }
61         }
62         return res;
63     }
64 }
View Code
 1 package LeetCode.test5_sousuosuanfa;
 2 
 3 import java.util.ArrayList;
 4 import java.util.LinkedList;
 5 import java.util.List;
 6 import java.util.Queue;
 7 
 8 /**
 9  * 思路:从边缘开始,先找到所有出度为1的节点,然后把所有出度为1的节点进队列,然后不断地bfs,
10  * 最后找到的就是两边同时向中间靠近的节点,那么这个中间节点就相当于把整个距离二分了,
11  * 那么它当然就是到两边距离最小的点啦,也就是到其他叶子节点最近的节点了。
12  * 出现的问题:超出内存限制
13  */
14 public class ques_310_最小高度树_2 {
15     public static void main(String[] args) {
16         int[][] edges = {{3, 0}, {3, 1}, {3, 2}, {3, 4}, {5, 4}};
17         int n = 6;
18         System.out.println(findMinHeightTrees(n, edges));
19     }
20 
21     public static List<Integer> findMinHeightTrees(int n, int[][] edges) {
22         List<Integer> res = new ArrayList<>();
23         // 如果只有一个节点,那么他就是最小高度树
24         if (n == 1) {
25             res.add(0);
26             return res;
27         }
28         // 建立图关系,在每个节点的list中存储相连节点
29         List<List<Integer>> map = new ArrayList<>();  // 索引为当前节点,索引所在list中的元素与索引为相邻节点
30         for (int i = 0; i < n; i++) {
31             map.add(new ArrayList<>());
32         }
33         // 建立各个节点的出度表
34         int[] degree = new int[n];
35 
36         for (int[] edge : edges) {
37             map.get(edge[0]).add(edge[1]);
38             map.get(edge[1]).add(edge[0]);
39             degree[edge[0]]++;
40             degree[edge[1]]++;
41         }
42         Queue<Integer> queue = new LinkedList<>();
43         for (int i = 0; i < n; i++) {  // 把所有出度为1的节点,也就是叶子节点入队
44             if (degree[i] == 1) {
45                 queue.offer(i);
46             }
47         }
48         while (!queue.isEmpty()) {
49             // 每层循环都要new一个新的结果集合,这样最后保存的就是最终的最小高度树了
50             res = new ArrayList<>();
51             int size = queue.size();
52             for (int i = 0; i < size; i++) {  // eg:假设最后只剩1或者2个节点
53                 int top = queue.poll();
54                 res.add(top);
55                 //经典bfs,把当前节点的相邻接点都拿出来
56                 List<Integer> neighbors = map.get(top);
57                 //把它们的出度都减1(当前节点已经不存在了),而它的相邻节点们就有可能变成叶子节点
58                 for (Integer neighbor : neighbors) {
59                     degree[neighbor]--;
60                     if (degree[neighbor] == 1) {
61                         queue.offer(neighbor);
62                     }
63                 }
64             }
65         }
66         return res;
67     }
68 }
View Code

7、迷宫找礼物

问题:给定一个二维数组,0代表能走,1代表不能走,8代表宝藏的位置,入口是二维数组的边界位置的任一个不为0的位置,求从人口到宝藏位置的最短路径,并输出路径

  1 package Interview;
  2 
  3 import java.util.*;
  4 
  5 class Node {
  6     int x;
  7     int y;
  8     Node pre;
  9 
 10     public Node(int x, int y) {
 11         this.x = x;
 12         this.y = y;
 13     }
 14 }
 15 
 16 // 问题:起点为maze四周为0的点(多个),终点为8,列出所有起点到终点的最短路径中最短的路径。
 17 public class test1 {
 18     int[] X = {0, 0, 1, -1};
 19     int[] Y = {1, -1, 0, 0};
 20     boolean[][] vis;
 21     ArrayList<Node> arrayList;
 22 
 23     public ArrayList<Node> winMazeGift(int[][] maze) {
 24         ArrayList<ArrayList<Node>> result = new ArrayList<>();
 25         for (int i = 0; i < maze.length; i++) {
 26             if (maze[i][0] == 0 || maze[i][0] == 8) {
 27                 result.add(mut(i, 0, maze));
 28             }
 29             if (maze[i][maze[0].length - 1] == 0 || maze[i][maze[0].length - 1] == 8) {
 30                 result.add(mut(i, maze[0].length - 1, maze));
 31             }
 32         }
 33 
 34         for (int i = 1; i < maze[0].length - 1; i++) {
 35             if (maze[0][i] == 0 || maze[0][i] == 8) {
 36                 result.add(mut(0, i, maze));
 37             }
 38             if (maze[maze.length - 1][i] == 0 || maze[maze.length - 1][i] == 8) {
 39                 result.add(mut(maze.length - 1, i, maze));
 40             }
 41         }
 42 
 43         int min = Integer.MAX_VALUE;
 44         for (ArrayList<Node> nodes : result) {
 45             min = Math.min(min, nodes.size());
 46         }
 47 
 48         int index = -1;
 49         for (int i = 0; i < result.size(); i++) {
 50             if (result.get(i).size() == min) {
 51                 index = i;
 52             }
 53         }
 54         // 测试
 55         for (int j = 0; j < min; j++) {
 56             Node pop = result.get(index).get(j);
 57             System.out.println("(" + pop.x + "," + pop.y + ")");
 58         }
 59 
 60         return index == -1 ? new ArrayList<>() : result.get(index);
 61     }
 62 
 63     public ArrayList<Node> mut(int x, int y, int[][] maze) {
 64         ArrayList<Node> res = new ArrayList<>();
 65         helper(x, y, maze);
 66         Stack<Node> stack = new Stack<>();
 67         print(arrayList.get(arrayList.size() - 1), stack);
 68         while (!stack.isEmpty()) {
 69             Node pop = stack.pop();
 70             res.add(pop);
 71 //            System.out.println("(" + pop.x + "," + pop.y + ")");
 72         }
 73         return res;
 74     }
 75 
 76 
 77     public void helper(int x, int y, int[][] maze) {
 78         vis = new boolean[maze.length][maze[0].length];
 79         Queue<Node> queue = new LinkedList<>();
 80         // 用来保存每一个出队列的节点
 81         arrayList = new ArrayList<>();
 82         queue.offer(new Node(x, y));
 83         vis[x][y] = true;
 84         while (!queue.isEmpty()) {
 85             Node top = queue.poll();
 86             arrayList.add(top);
 87             if (maze[top.x][top.y] == 8) {
 88                 return;
 89             }
 90             for (int i = 0; i < 4; i++) {
 91                 int newX = top.x + X[i];
 92                 int newY = top.y + Y[i];
 93                 if (judge(newX, newY, maze)) {
 94                     vis[newX][newY] = true;
 95                     Node node = new Node(newX, newY);
 96                     queue.offer(node);
 97                     node.pre = top;
 98                 }
 99             }
100         }
101     }
102 
103     public boolean judge(int newX, int newY, int[][] maze) {
104         if (newX < 0 || newY < 0 || newX >= maze.length || newY >= maze[0].length) {
105             return false;
106         }
107         if (maze[newX][newY] == 1 || vis[newX][newY]) {
108             return false;
109         }
110         return true;
111     }
112 
113     public void print(Node node, Stack<Node> stack) {
114         stack.push(node);
115         if (node.pre != null) {
116             print(node.pre, stack);
117         }
118     }
119 
120 }
121 
122 class Test_test1 {
123     public static void main(String[] args) {
124         test1 t = new test1();
125 
126         int[][] maze = {{0, 1, 1, 1},
127                 {0, 0, 0, 1},
128                 {1, 0, 8, 1},
129                 {1, 0, 1, 1}};
130 
131 //        int[][] maze = {{0, 1, 1, 1},
132 //                {8, 0, 0, 1},
133 //                {1, 0, 0, 1},
134 //                {1, 0, 1, 1}};
135 
136 //        int[][] maze = {{0, 0, 1, 1, 1, 1, 1, 1, 1, 1},
137 //                {1, 0, 0, 1, 0, 0, 0, 1, 0, 1},
138 //                {1, 0, 0, 1, 0, 0, 0, 1, 0, 1},
139 //                {1, 0, 0, 0, 0, 1, 1, 0, 0, 1},
140 //                {1, 0, 1, 1, 1, 0, 0, 0, 0, 1},
141 //                {1, 0, 0, 0, 1, 0, 0, 0, 0, 1},
142 //                {1, 0, 1, 0, 0, 0, 1, 0, 0, 1},
143 //                {1, 0, 1, 1, 1, 0, 1, 1, 0, 1},
144 //                {1, 1, 0, 0, 0, 0, 0, 0, 8, 1},
145 //                {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}};
146 
147         System.out.println(t.winMazeGift(maze));
148     }
149 }
View Code

8、电话号码的自由组合

问题:给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按任意顺序返回。

给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。

 

示例 1:

输入:digits = "23"
输出:["ad","ae","af","bd","be","bf","cd","ce","cf"]
示例 2:

输入:digits = ""
输出:[]
示例 3:

输入:digits = "2"
输出:["a","b","c"]

 1 class Solution {
 2     public List<String> letterCombinations(String digits) {
 3         List<String> result = new ArrayList<>();
 4         if(digits == null || digits.isEmpty()){
 5             return result;
 6         }
 7         char[][] temp = {{'a','b','c'},{'d','e','f'},{'g','h','i'},{'j','k','l'},{'m','n','o'},{'p','q','r','s'},{'t','u','v'},{'w','x','y','z'}};
 8         Queue<String> queue = new LinkedList<>();
 9         queue.add("");
10         while(queue.peek().length()!=digits.length()){
11             String top = queue.poll();
12             char[] temps = temp[digits.charAt(top.length()) - '2'];
13             for(int i=0;i<temps.length;i++){
14                 queue.offer(top+temps[i]);
15             }
16         }
17         while(!queue.isEmpty()){
18             result.add(queue.poll());
19         }
20         return result;
21     }
22 }
View Code

 

posted on 2021-12-30 14:49  晨曦生辉耀匕尖  阅读(170)  评论(0编辑  收藏  举报