BFS

广度优先搜索一层一层地进行遍历,每层遍历都是以上一层遍历的结果作为起点,遍历一个距离能访问到的所有节点。需要注意的是,遍历过的节点不能再次被遍历。

第一层:

  • 0 -> {6,2,1,5}

第二层:

  • 6 -> {4}
  • 2 -> {}
  • 1 -> {}
  • 5 -> {3}

第三层:

  • 4 -> {}
  • 3 -> {}

每一层遍历的节点都与根节点距离相同。设 di 表示第 i 个节点与根节点的距离,推导出一个结论:对于先遍历的节点 i 与后遍历的节点 j,有 di <= dj。利用这个结论,可以求解最短路径等 最优解 问题:第一次遍历到目的节点,其所经过的路径为最短路径。应该注意的是,使用 BFS 只能求解无权图的最短路径,无权图是指从一个节点到另一个节点的代价都记为 1。

在程序实现 BFS 时需要考虑以下问题:

  • 队列:用来存储每一轮遍历得到的节点;
  • 标记:对于遍历过的节点,应该将它标记,防止重复遍历。

1. 计算在网格中从原点到特定点的最短路径长度

1091. Shortest Path in Binary Matrix(Medium)

Leetcode / 力扣

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

题目描述:0 表示可以经过某个位置,求解从左上角到右下角的最短路径长度。

  1. class Solution {
  2. public static int shortestPathBinaryMatrix(int[][] grid) {
  3. if (grid == null || grid.length == 0 || grid[0].length == 0) {
  4. return -1;
  5. }
  6. // 如果起点就阻塞那就玩完啦
  7. if (grid[0][0]==1){
  8. return -1;
  9. }
  10. //定义 8个方向
  11. int[][] dir = {{1, -1}, {1, 0}, {1, 1}, {0,-1},{0,1},{-1,-1},{-1,0},{-1,1}};
  12. int m = grid.length;
  13. int n = grid[0].length;
  14. //bfs的老套路 来个队列
  15. Queue<int[]> queue = new LinkedList<>();
  16. queue.add(new int[]{0,0}); //把起点扔进去
  17. grid[0][0] = 1; // 把起点标记为阻塞
  18. int path = 1; // 层数
  19. while (!queue.isEmpty()){
  20. int size = queue.size();
  21. while(size-- > 0){
  22. int[] cur = queue.poll();
  23. int x = cur[0];
  24. int y = cur[1];
  25. //能放进队列里的都是为0可以走的(这一点在后面保证了)
  26. // 如果等于终点则返回
  27. if (x == m-1 && y == n-1){ //
  28. return path;
  29. }
  30. //开始八个方向的判断
  31. for (int[] d : dir){
  32. int x1 = x + d[0];
  33. int y1 = y + d[1];
  34. //这里开始过滤
  35. if (x1 < 0 || x1 >= m || y1 < 0||y1>=n || grid[x1][y1]==1){
  36. continue;
  37. }
  38. //把在数组范围内 并且为0不阻塞的放入队列中
  39. queue.add(new int[]{x1,y1});
  40. grid[x1][y1] = 1; // 标记
  41. }
  42. }
  43. path++; //遍历完一层 这时候要 ++啦
  44. }
  45. return -1;
  46. }
  47. }

2. 组成整数的最小平方数数量

279. Perfect Squares (Medium)

Leetcode / 力扣

  1. For example, given n = 12, return 3 because 12 = 4 + 4 + 4; given n = 13, return 2 because 13 = 4 + 9.

可以将每个整数看成图中的一个节点,如果两个整数之差为一个平方数,那么这两个整数所在的节点就有一条边。

要求解最小的平方数数量,就是求解从节点 n 到节点 0 的最短路径。

本题也可以用动态规划求解,在之后动态规划部分中会再次出现。

  1. class Solution {
  2. public int numSquares(int n) {
  3. ArrayList<Integer> square_nums = new ArrayList<Integer>();
  4. for (int i = 1; i * i <= n; ++i) {
  5. square_nums.add(i * i);
  6. }
  7. Set<Integer> queue = new HashSet<Integer>();
  8. queue.add(n);
  9. int level = 0;
  10. while (queue.size() > 0) {
  11. level += 1;
  12. Set<Integer> next_queue = new HashSet<Integer>();
  13. for (Integer remainder : queue) {
  14. for (Integer square : square_nums) {
  15. if (remainder.equals(square)) {
  16. return level;
  17. } else if (remainder < square) {
  18. break;
  19. } else {
  20. next_queue.add(remainder - square);
  21. }
  22. }
  23. }
  24. queue = next_queue;
  25. }
  26. return level;
  27. }
  28. }

3. 最短单词路径

127. Word Ladder (Medium)

Leetcode / 力扣

  1. Input:
  2. beginWord = "hit",
  3. endWord = "cog",
  4. wordList = ["hot","dot","dog","lot","log","cog"]
  5. Output: 5
  6. Explanation: As one shortest transformation is "hit" -> "hot" -> "dot" -> "dog" -> "cog",
  7. return its length 5.
  1. Input:
  2. beginWord = "hit"
  3. endWord = "cog"
  4. wordList = ["hot","dot","dog","lot","log"]
  5. Output: 0
  6. Explanation: The endWord "cog" is not in wordList, therefore no possible transformation.

题目描述:找出一条从 beginWord 到 endWord 的最短路径,每次移动规定为改变一个字符,并且改变之后的字符串必须在 wordList 中。

  1. class Solution {
  2. public int ladderLength(String beginWord, String endWord, List<String> wordList) {
  3. if (!wordList.contains(endWord)) {
  4. return 0;
  5. }
  6. // visited修改为boolean数组
  7. boolean[] visited = new boolean[wordList.size()];
  8. int idx = wordList.indexOf(beginWord);
  9. if (idx != -1) {
  10. visited[idx] = true;
  11. }
  12. Queue<String> queue = new LinkedList<>();
  13. queue.add(beginWord);
  14. int count = 0;
  15. while (queue.size() > 0) {
  16. int size = queue.size();
  17. ++count;
  18. while (size-- > 0) {
  19. String start = queue.poll();
  20. for (int i = 0; i < wordList.size(); ++i) {
  21. // 通过index判断是否已经访问
  22. if (visited[i]) {
  23. continue;
  24. }
  25. String s = wordList.get(i);
  26. if (!canConvert(start, s)) {
  27. continue;
  28. }
  29. if (s.equals(endWord)) {
  30. return count + 1;
  31. }
  32. visited[i] = true;
  33. queue.offer(s);
  34. }
  35. }
  36. }
  37. return 0;
  38. }
  39. public boolean canConvert(String s1, String s2) {
  40. // 因为题目说了单词长度相同,可以不考虑长度问题
  41. // if (s1.length() != s2.length()) return false;
  42. int count = 0;
  43. for (int i = 0; i < s1.length(); ++i) {
  44. if (s1.charAt(i) != s2.charAt(i)) {
  45. ++count;
  46. if (count > 1) {
  47. return false;
  48. }
  49. }
  50. }
  51. return count == 1;
  52. }
  53. }

DFS

广度优先搜索一层一层遍历,每一层得到的所有新节点,要用队列存储起来以备下一层遍历的时候再遍历。

而深度优先搜索在得到一个新节点时立即对新节点进行遍历:从节点 0 出发开始遍历,得到到新节点 6 时,立马对新节点 6 进行遍历,得到新节点 4;如此反复以这种方式遍历新节点,直到没有新节点了,此时返回。返回到根节点 0 的情况是,继续对根节点 0 进行遍历,得到新节点 2,然后继续以上步骤。

从一个节点出发,使用 DFS 对一个图进行遍历时,能够遍历到的节点都是从初始节点可达的,DFS 常用来求解这种 可达性 问题。

在程序实现 DFS 时需要考虑以下问题:

  • 栈:用栈来保存当前节点信息,当遍历新节点返回时能够继续遍历当前节点。可以使用递归栈。
  • 标记:和 BFS 一样同样需要对已经遍历过的节点进行标记。

1. 查找最大的连通面积

695. Max Area of Island (Medium)

Leetcode / 力扣

  1. [[0,0,1,0,0,0,0,1,0,0,0,0,0],
  2. [0,0,0,0,0,0,0,1,1,1,0,0,0],
  3. [0,1,1,0,1,0,0,0,0,0,0,0,0],
  4. [0,1,0,0,1,1,0,0,1,0,1,0,0],
  5. [0,1,0,0,1,1,0,0,1,1,1,0,0],
  6. [0,0,0,0,0,0,0,0,0,0,1,0,0],
  7. [0,0,0,0,0,0,0,1,1,1,0,0,0],
  8. [0,0,0,0,0,0,0,1,1,0,0,0,0]]
  1. private int m, n;
  2. private int[][] direction = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
  3. public int maxAreaOfIsland(int[][] grid) {
  4. if (grid == null || grid.length == 0) {
  5. return 0;
  6. }
  7. m = grid.length;
  8. n = grid[0].length;
  9. int maxArea = 0;
  10. for (int i = 0; i < m; i++) {
  11. for (int j = 0; j < n; j++) {
  12. maxArea = Math.max(maxArea, dfs(grid, i, j));
  13. }
  14. }
  15. return maxArea;
  16. }
  17. private int dfs(int[][] grid, int r, int c) {
  18. if (r < 0 || r >= m || c < 0 || c >= n || grid[r][c] == 0) {
  19. return 0;
  20. }
  21. grid[r][c] = 0;
  22. int area = 1;
  23. for (int[] d : direction) {
  24. area += dfs(grid, r + d[0], c + d[1]);
  25. }
  26. return area;
  27. }

2. 矩阵中的连通分量数目

200. Number of Islands (Medium)

Leetcode / 力扣

  1. Input:
  2. 11000
  3. 11000
  4. 00100
  5. 00011
  6. Output: 3

可以将矩阵表示看成一张有向图。

这俩个题目好典型,要结合在一起学会使用

  1. class Solution {
  2. private int m, n;
  3. private int[][] direction={{0,1},{0,-1},{1,0},{-1,0}};
  4. public int numIslands(char[][] grid) {
  5. if(grid==null||grid.length==0){
  6. return 0;
  7. }
  8. m=grid.length;
  9. n=grid[0].length;
  10. int islandsNum = 0;
  11. for(int i=0;i<m;i++){
  12. for(int j=0;j<n;j++){
  13. if(grid[i][j]!='0'){
  14. dfs(grid,i,j);
  15. islandsNum++;
  16. }
  17. }
  18. }
  19. return islandsNum;
  20. }
  21. private void dfs(char[][] grid,int i ,int j){
  22. if(i<0|| i>=m||j<0||j>=n||grid[i][j]=='0'){
  23. return;
  24. }
  25. grid[i][j]='0';
  26. for(int[] d:direction){
  27. dfs(grid,i+d[0],j+d[1]);
  28. }
  29. }
  30. }

3. 好友关系的连通分量数目

547. Friend Circles (Medium)

Leetcode / 力扣

算法

给定的矩阵可以看成图的邻接矩阵。这样我们的问题可以变成无向图连通块的个数。为了方便理解,考虑如下矩阵:

  1. M= [1 1 0 0 0 0
  2. 1 1 0 0 0 0
  3. 0 0 1 1 1 0
  4. 0 0 1 1 0 0
  5. 0 0 1 0 1 0
  6. 0 0 0 0 0 1]

题目给出的矩阵实际上是一个邻接矩阵,因此这个题目就抽象成了已知邻接矩阵,求这个图的连通分量个数这样一个问题,就是一个模板题了,直接套模板代码即可。

  1. class Solution {
  2. private int n;
  3. public int findCircleNum(int[][] M) {
  4. n=M.length;
  5. int circleNum=0;
  6. boolean[] hasVisited=new boolean[n];
  7. for(int i=0;i<n;i++){
  8. if(!hasVisited[i]){
  9. dfs(M,i,hasVisited);
  10. circleNum++;
  11. }
  12. }
  13. return circleNum;
  14. }
  15. private void dfs(int[][] M,int i,boolean[] hasVisited){
  16. hasVisited[i]=true ;
  17. for(int k=0;k<n;k++){
  18. if(M[i][k]==1&&!hasVisited[k]){
  19. dfs(M,k,hasVisited);
  20. }
  21. }
  22. }
  23. }

4. 填充封闭区域

130. Surrounded Regions (Medium)

Leetcode / 力扣

思路及解法

我们可以使用深度优先搜索实现标记操作。在下面的代码中,我们把标记过的字母 O 修改为字母 T

  1. private int[][] direction = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
  2. private int m, n;
  3. public void solve(char[][] board) {
  4. if (board == null || board.length == 0) {
  5. return;
  6. }
  7. m = board.length;
  8. n = board[0].length;
  9. for (int i = 0; i < m; i++) {
  10. dfs(board, i, 0);
  11. dfs(board, i, n - 1);
  12. }
  13. for (int i = 0; i < n; i++) {
  14. dfs(board, 0, i);
  15. dfs(board, m - 1, i);
  16. }
  17. for (int i = 0; i < m; i++) {
  18. for (int j = 0; j < n; j++) {
  19. if (board[i][j] == 'T') {
  20. board[i][j] = 'O';
  21. } else if (board[i][j] == 'O') {
  22. board[i][j] = 'X';
  23. }
  24. }
  25. }
  26. }
  27. private void dfs(char[][] board, int r, int c) {
  28. if (r < 0 || r >= m || c < 0 || c >= n || board[r][c] != 'O') {
  29. return;
  30. }
  31. board[r][c] = 'T';
  32. for (int[] d : direction) {
  33. dfs(board, r + d[0], c + d[1]);
  34. }
  35. }

5. 能到达的太平洋和大西洋的区域

417. Pacific Atlantic Water Flow (Medium)

Leetcode / 力扣

  1. Given the following 5x5 matrix:
  2. Pacific ~ ~ ~ ~ ~
  3. ~ 1 2 2 3 (5) *
  4. ~ 3 2 3 (4) (4) *
  5. ~ 2 4 (5) 3 1 *
  6. ~ (6) (7) 1 4 5 *
  7. ~ (5) 1 1 2 4 *
  8. * * * * * Atlantic
  9. Return:
  10. [[0, 4], [1, 3], [1, 4], [2, 2], [3, 0], [3, 1], [4, 0]] (positions with parentheses in above matrix).

左边和上边是太平洋,右边和下边是大西洋,内部的数字代表海拔,海拔高的地方的水能够流到低的地方,求解水能够流到太平洋和大西洋的所有位置。

  1. class Solution {
  2. private int m,n;
  3. private int[][] matrix;
  4. private int[][] direction={{0,1},{0,-1},{1,0},{-1,0}};
  5. public List<List<Integer>> pacificAtlantic(int[][] matrix) {
  6. List<List<Integer>> ret =new ArrayList<>();
  7. if(matrix == null || matrix.length==0){
  8. return ret;
  9. }
  10. m=matrix.length;
  11. n=matrix[0].length;
  12. this.matrix=matrix;
  13. boolean[][] canReachP=new boolean[m][n];
  14. boolean[][] canReachA=new boolean[m][n];
  15. for(int i=0;i<m;i++){
  16. dfs(i,0,canReachP);
  17. dfs(i,n-1,canReachA);
  18. }
  19. for(int i=0;i<n;i++){
  20. dfs(0,i,canReachP);
  21. dfs(m-1,i,canReachA);
  22. }
  23. for(int i=0;i<m;i++){
  24. for(int j=0;j<n;j++){
  25. if(canReachP[i][j]&&canReachA[i][j]){
  26. ret.add(Arrays.asList(i,j));
  27. }
  28. }
  29. }
  30. return ret;
  31. }
  32. private void dfs(int r,int c,boolean[][] canReach){
  33. if(canReach[r][c]){
  34. return;
  35. }
  36. canReach[r][c]=true;
  37. for(int[] d: direction){
  38. int nextR=d[0]+r;
  39. int nextC=d[1]+c;
  40. if(nextR<0||nextR>=m||nextC<0||nextC>=n||matrix[r][c]>matrix[nextR][nextC]){
  41. continue;
  42. }
  43. dfs(nextR,nextC,canReach);
  44. }
  45. }
  46. }

Backtracking

Backtracking(回溯)属于 DFS。

  • 普通 DFS 主要用在 可达性问题 ,这种问题只需要执行到特点的位置然后返回即可。
  • 而 Backtracking 主要用于求解 排列组合 问题,例如有 { 'a','b','c' } 三个字符,求解所有由这三个字符排列得到的字符串,这种问题在执行到特定的位置返回之后还会继续执行求解过程。

因为 Backtracking 不是立即返回,而要继续求解,因此在程序实现时,需要注意对元素的标记问题:

  • 在访问一个新元素进入新的递归调用时,需要将新元素标记为已经访问,这样才能在继续递归调用时不用重复访问该元素;
  • 但是在递归返回时,需要将元素标记为未访问,因为只需要保证在一个递归链中不同时访问一个元素,可以访问已经访问过但是不在当前递归链中的元素。

1. 数字键盘组合

17. Letter Combinations of a Phone Number (Medium)

Leetcode / 力扣

  1. Input:Digit string "23"
  2. Output: ["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"].
  1. class Solution {
  2. private String[] Keys={"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
  3. public List<String> letterCombinations(String digits) {
  4. List<String>combinations=new ArrayList<>();
  5. if(digits==null||digits.length()==0){
  6. return combinations;
  7. }
  8. doCombination(new StringBuilder(),combinations,digits);
  9. return combinations;
  10. }
  11. private void doCombination(StringBuilder prefix,List<String>combinations,String digits){
  12. if(prefix.length()==digits.length()){
  13. combinations.add(prefix.toString());
  14. return;
  15. }
  16. int curDigits=digits.charAt(prefix.length())-'0';
  17. String letters=Keys[curDigits];
  18. for(char c: letters.toCharArray()){
  19. prefix.append(c);
  20. doCombination(prefix,combinations,digits);
  21. prefix.deleteCharAt(prefix.length()-1);
  22. }
  23. }
  24. }

2. IP 地址划分

93. Restore IP Addresses(Medium)

Leetcode / 力扣

  1. Given "25525511135",
  2. return ["255.255.11.135", "255.255.111.35"].
  1. class Solution {
  2. public List<String> restoreIpAddresses(String s) {
  3. List<String> ans = new ArrayList<>();
  4. if (s == null || s.length() == 0) {
  5. return ans;
  6. }
  7. backtrack(s, ans, 0, new ArrayList<>());
  8. return ans;
  9. }
  10. // pos-当前遍历到 s 字符串中的位置,tmp-当前存放已经确定好的 ip 段的数量
  11. private void backtrack(String s, List<String> ans, int pos, List<String> tmp) {
  12. if (tmp.size() == 4) {
  13. // 如果此时 pos 也刚好遍历完整个 s
  14. if (pos == s.length()) {
  15. // join 用法:例如 [[255],[255],[111],[35]] -> 255.255.111.35
  16. ans.add(String.join(".", tmp));
  17. }
  18. // 否则直接返回
  19. return;
  20. }
  21. // ip 地址每段最多有三个数字
  22. for (int i = 1; i <= 3; i++) {
  23. // 如果当前位置距离 s 末尾小于 3 就不用再分段了,直接跳出循环即可。
  24. if (pos + i > s.length()) {
  25. break;
  26. }
  27. // 将 s 的子串开始分段
  28. String segment = s.substring(pos, pos + i);
  29. int val = Integer.valueOf(segment);
  30. // 剪枝条件:段的起始位置不能为 0,段拆箱成 int 类型的长度不能大于 255
  31. if (segment.startsWith("0") && segment.length() > 1 || (i == 3 && val > 255)) {
  32. continue;
  33. }
  34. // 符合要求就加入到 tmp 中
  35. tmp.add(segment);
  36. // 继续递归遍历下一个位置
  37. backtrack(s, ans, pos + i, tmp);
  38. // 回退到上一个元素,即回溯
  39. tmp.remove(tmp.size() - 1);
  40. }
  41. }
  42. }
  1. public List<String> restoreIpAddresses(String s) {
  2. List<String> addresses = new ArrayList<>();
  3. StringBuilder tempAddress = new StringBuilder();
  4. doRestore(0, tempAddress, addresses, s);
  5. return addresses;
  6. }
  7. private void doRestore(int k, StringBuilder tempAddress, List<String> addresses, String s) {
  8. if (k == 4 || s.length() == 0) {
  9. if (k == 4 && s.length() == 0) {
  10. addresses.add(tempAddress.toString());
  11. }
  12. return;
  13. }
  14. for (int i = 0; i < s.length() && i <= 2; i++) {
  15. if (i != 0 && s.charAt(0) == '0') {
  16. break;
  17. }
  18. String part = s.substring(0, i + 1);
  19. if (Integer.valueOf(part) <= 255) {
  20. if (tempAddress.length() != 0) {
  21. part = "." + part;
  22. }
  23. tempAddress.append(part);
  24. doRestore(k + 1, tempAddress, addresses, s.substring(i + 1));
  25. tempAddress.delete(tempAddress.length() - part.length(), tempAddress.length());
  26. }
  27. }
  28. }

3. 在矩阵中寻找字符串

79. Word Search (Medium)

Leetcode / 力扣

  1. For example,
  2. Given board =
  3. [
  4. ['A','B','C','E'],
  5. ['S','F','C','S'],
  6. ['A','D','E','E']
  7. ]
  8. word = "ABCCED", -> returns true,
  9. word = "SEE", -> returns true,
  10. word = "ABCB", -> returns false.
  1. class Solution {
  2. private int[][] direction={{1,0},{-1,0},{0,1},{0,-1}};
  3. private int m;
  4. private int n;
  5. public boolean exist(char[][] board, String word) {
  6. if(word==null||word.length()==0){
  7. return true;
  8. }
  9. if(board==null||board.length==0||board[0].length==0){
  10. return false;
  11. }
  12. m=board.length;
  13. n=board[0].length;
  14. boolean[][] hasVisited = new boolean[m][n];
  15. for(int i=0;i<m;i++){
  16. for(int j=0;j<n;j++){
  17. if(backtracking(0,i,j,hasVisited,board,word)){
  18. return true;
  19. }
  20. }
  21. }
  22. return false;
  23. }
  24. private boolean backtracking(int curLen,int i, int j, boolean[][]hasVisited,char[][] board,String word){
  25. //记录word的长度,长度相同则认为匹配
  26. if(curLen==word.length()){
  27. return true;
  28. }
  29. if(i<0||i>=m||j<0||j>=n||board[i][j]!=word.charAt(curLen)||hasVisited[i][j]){
  30. return false;
  31. }
  32. hasVisited[i][j]=true;
  33. for(int[] d: direction){
  34. if(backtracking(curLen+1,i+d[0],j+d[1],hasVisited,board,word)){
  35. return true;
  36. }
  37. }
  38. hasVisited[i][j] = false;
  39. return false;
  40. }
  41. }

4. 输出二叉树中所有从根到叶子的路径

257. Binary Tree Paths (Easy)

Leetcode / 力扣

  1. 1
  2. / \
  3. 2 3
  4. \
  5. 5
  1. ["1->2->5", "1->3"]
  1. /**
  2. * Definition for a binary tree node.
  3. * public class TreeNode {
  4. * int val;
  5. * TreeNode left;
  6. * TreeNode right;
  7. * TreeNode(int x) { val = x; }
  8. * }
  9. */
  10. class Solution {
  11. public List<String> binaryTreePaths(TreeNode root) {
  12. List<String> tree_list=new ArrayList<>();
  13. if(root==null){
  14. return tree_list;
  15. }
  16. String bree="";
  17. backtrace(root ,tree_list,bree);
  18. return tree_list;
  19. }
  20. private void backtrace(TreeNode root ,List<String> tree_list,String bree){
  21. if(root==null){
  22. return;
  23. }
  24. bree+=root.val;
  25. if(root.left==null&&root.right==null){
  26. tree_list.add(bree);
  27. }
  28. else{
  29. bree+="->";
  30. backtrace(root.left,tree_list,bree);
  31. backtrace(root.right,tree_list,bree);;
  32. }
  33. }
  34. }

5. 排列

46. Permutations (Medium)

Leetcode / 力扣

  1. [1,2,3] have the following permutations:
  2. [
  3. [1,2,3],
  4. [1,3,2],
  5. [2,1,3],
  6. [2,3,1],
  7. [3,1,2],
  8. [3,2,1]
  9. ]
  1. class Solution {
  2. public List<List<Integer>> permute(int[] nums) {
  3. List<List<Integer>> res = new ArrayList<List<Integer>>();
  4. List<Integer> output = new ArrayList<Integer>();
  5. for (int num : nums) {
  6. output.add(num);
  7. }
  8. int n = nums.length;
  9. backtrack(n, output, res, 0);
  10. return res;
  11. }
  12. public void backtrack(int n, List<Integer> output, List<List<Integer>> res, int first) {
  13. // 所有数都填完了
  14. if (first == n) {
  15. res.add(new ArrayList<Integer>(output));
  16. }
  17. for (int i = first; i < n; i++) {
  18. // 动态维护数组
  19. Collections.swap(output, first, i);
  20. // 继续递归填下一个数
  21. backtrack(n, output, res, first + 1);
  22. // 撤销操作
  23. Collections.swap(output, first, i);
  24. }
  25. }
  26. }
  1. class Solution {
  2. public List<List<Integer>> permute(int[] nums) {
  3. List<List<Integer>> permutes = new ArrayList<>();
  4. List<Integer> permuteList = new ArrayList<>();
  5. boolean[] hasVisited = new boolean[nums.length];
  6. backtracking(permuteList, permutes, hasVisited, nums);
  7. return permutes;
  8. }
  9. private void backtracking(List<Integer> permuteList, List<List<Integer>> permutes, boolean[] visited, final int[] nums) {
  10. if (permuteList.size() == nums.length) {
  11. permutes.add(new ArrayList<>(permuteList)); // 重新构造一个 List
  12. return;
  13. }
  14. for (int i = 0; i < visited.length; i++) {
  15. if (visited[i]) {
  16. continue;
  17. }
  18. visited[i] = true;
  19. permuteList.add(nums[i]);
  20. backtracking(permuteList, permutes, visited, nums);
  21. permuteList.remove(permuteList.size() - 1);
  22. visited[i] = false;
  23. }
  24. }
  25. }

6. 含有相同元素求排列

47. Permutations II (Medium)

Leetcode / 力扣

  1. [1,1,2] have the following unique permutations:
  2. [[1,1,2], [1,2,1], [2,1,1]]

数组元素可能含有相同的元素,进行排列时就有可能出现重复的排列,要求重复的排列只返回一个。

在实现上,和 Permutations 不同的是要先排序,然后在添加一个元素时,判断这个元素是否等于前一个元素,如果等于,并且前一个元素还未访问,那么就跳过这个元素。

  1. class Solution {
  2. public List<List<Integer>> permuteUnique(int[] nums) {
  3. List<List<Integer>> permutes=new ArrayList<>();
  4. List<Integer> permuteList=new ArrayList<>();
  5. Arrays.sort(nums);
  6. boolean hasVisited =new boolean[nums.length];
  7. backtracking(permuteList,permutes,hasVisited,nums);
  8. return permutes;
  9. }
  10. private void backtracking(List<Integer> permuteList,List<List<Integer>>permutes,boolean[] visited,int[] nums){
  11. if(permuteList.size()==nums.length){
  12. permutes.add(new ArrayList<>(permuteList));
  13. return;
  14. }
  15. for(int i=0;i<visited.length;i++){
  16. if(i!=0&&nums[i]==nums[i-1]&&!visited[i-1]){
  17. continue;
  18. }
  19. if(visited[i]){
  20. continue;
  21. }
  22. visited[i]=true;
  23. permuteList.add(nums[i]);
  24. backtracking(permuteList,permutes,visited,nums);
  25. permuteList.remove(permuteList.size()-1);
  26. visited[i]=false;
  27. }
  28. }
  29. }

7. 组合

77. Combinations (Medium)

Leetcode / 力扣

  1. class Solution {
  2. public List<List<Integer>> combine(int n, int k) {
  3. List<List<Integer>> result =new ArrayList<>();
  4. if(n==0||k==0){
  5. return result;
  6. }
  7. LinkedList<Integer> arr=new LinkedList<>();
  8. backtrcak(n,k,1,arr,result);
  9. return result;
  10. }
  11. //prenum为访问的第几个数字
  12. public void backtrcak(int n,int k,int prenum,LinkedList<Integer> arr,List<List<Integer>> result){
  13. if(arr.size()==k){
  14. result.add(new LinkedList<>(arr));
  15. return;
  16. }
  17. for(int i=prenum;i<=n;i++){
  18. arr.addLast(i);
  19. backtrcak(n,k,i+1,arr,result);
  20. arr.pollLast();
  21. }
  22. }
  23. }

8. 组合求和

39. Combination Sum (Medium)

Leetcode / 力扣

  1. given candidate set [2, 3, 6, 7] and target 7,
  2. A solution set is:
  3. [[7],[2, 2, 3]]
  1. class Solution {
  2. public List<List<Integer>> combinationSum(int[] candidates, int target) {
  3. List<Integer>arr=new ArrayList<>();
  4. List<List<Integer>> result=new ArrayList<>();
  5. if(candidates==null||candidates.length==0){
  6. return result;
  7. }
  8. backtrack(arr,result,candidates,target,0,0);
  9. return result;
  10. }
  11. private void backtrack(List<Integer>arr,List<List<Integer>> result,int[] candidates,int target,int sum,int start){
  12. if(target<sum)return;
  13. if(sum==target){
  14. result.add(new ArrayList<>(arr));
  15. return;
  16. }
  17. for(int i=start;i<candidates.length;i++){
  18. arr.add(candidates[i]);
  19. sum+=candidates[i];
  20. backtrack(arr,result,candidates,target,sum,i);
  21. sum-=candidates[i];
  22. arr.remove(arr.size()-1);
  23. }
  24. }
  25. }
  1. List<List<Integer>> lists = new ArrayList<>();
  2. public List<List<Integer>> combinationSum(int[] candidates, int target) {
  3. if (candidates == null || candidates.length == 0 || target < 0) {
  4. return lists;
  5. }
  6. List<Integer> list = new ArrayList<>();
  7. process(0, candidates, target, list);
  8. return lists;
  9. }
  10. private void process(int start, int[] candidates, int target, List<Integer> list) {
  11. //递归的终止条件
  12. if (target < 0) {
  13. return;
  14. }
  15. if (target == 0) {
  16. lists.add(new ArrayList<>(list));
  17. } else {
  18. for (int i = start; i < candidates.length; i++) {
  19. list.add(candidates[i]);
  20. //因为每个数字都可以使用无数次,所以递归还可以从当前元素开始
  21. process(i, candidates, target - candidates[i], list);
  22. list.remove(list.size() - 1);
  23. }
  24. }
  25. }

9. 含有相同元素的组合求和

40. Combination Sum II (Medium)

Leetcode / 力扣

  1. For example, given candidate set [10, 1, 2, 7, 6, 1, 5] and target 8,
  2. A solution set is:
  3. [
  4. [1, 7],
  5. [1, 2, 5],
  6. [2, 6],
  7. [1, 1, 6]
  8. ]
  1. class Solution {
  2. public List<List<Integer>> combinationSum2(int[] candidates, int target) {
  3. List<List<Integer>> result =new ArrayList<>();
  4. if(candidates.length==0||candidates==null){
  5. return result;
  6. }
  7. List<Integer>arr =new ArrayList<>();
  8. Arrays.sort(candidates);
  9. backtrack(candidates,arr,result,target,0);
  10. return result;
  11. }
  12. private void backtrack(int[] candidates,List<Integer>arr,List<List<Integer>> result,int target,int start){
  13. if(target==0){
  14. result.add(new ArrayList<>(arr));
  15. return;
  16. }
  17. if(target<0){
  18. return;
  19. }
  20. for(int i=start;i<candidates.length;i++){
  21. if(i>start&&candidates[i]==candidates[i-1]){
  22. continue;
  23. }
  24. arr.add(candidates[i]);
  25. backtrack(candidates,arr,result,target-candidates[i],i+1);
  26. arr.remove(arr.size()-1);
  27. }
  28. }
  29. }

10. 1-9 数字的组合求和

216. Combination Sum III (Medium)

Leetcode / 力扣

  1. Input: k = 3, n = 9
  2. Output:
  3. [[1,2,6], [1,3,5], [2,3,4]]

从 1-9 数字中选出 k 个数不重复的数,使得它们的和为 n。

  1. class Solution {
  2. public List<List<Integer>> combinationSum3(int k, int n) {
  3. List<List<Integer>>result=new ArrayList<>();
  4. List<Integer> path=new ArrayList<>();
  5. backtrack(k,n,1,path,result);
  6. return result;
  7. }
  8. private void backtrack(int k,int n, int start,List<Integer>path,List<List<Integer>>result){
  9. if(k==0&&n==0){
  10. result.add(new ArrayList<>(path));
  11. return;
  12. }
  13. if(k==0||n==0){
  14. return ;
  15. }
  16. for(int i=start;i<=9;i++){
  17. path.add(i);
  18. backtrack(k-1,n-i,i+1,path,result);
  19. path.remove(path.size()-1);
  20. }
  21. }
  22. }

11. 子集

78. Subsets (Medium)

Leetcode / 力扣

找出集合的所有子集,子集不能重复,[1, 2] 和 [2, 1] 这种子集算重复

  1. class Solution {
  2. public List<List<Integer>> subsets(int[] nums) {
  3. List<List<Integer>> result=new ArrayList<>();
  4. List<Integer> arr=new ArrayList<>();
  5. if(nums==null||nums.length==0){
  6. return result;
  7. }
  8. backtrack(0,nums,result,arr);
  9. return result;
  10. }
  11. private void backtrack(int i,int[] nums,List<List<Integer>> result,List<Integer> arr){
  12. result.add(new ArrayList<>(arr));
  13. for(int j=i;j<nums.length;j++){
  14. arr.add(nums[j]);
  15. backtrack(j+1,nums,result,arr);
  16. arr.remove(arr.size()-1);
  17. }
  18. }
  19. }

12. 含有相同元素求子集

90. Subsets II (Medium)

Leetcode / 力扣

  1. For example,
  2. If nums = [1,2,2], a solution is:
  3. [
  4. [2],
  5. [1],
  6. [1,2,2],
  7. [2,2],
  8. [1,2],
  9. []
  10. ]
  1. class Solution {
  2. public List<List<Integer>> subsetsWithDup(int[] nums) {
  3. List<List<Integer>>result=new ArrayList<>();
  4. List<Integer>arr=new ArrayList<>();
  5. if(nums.length==0||nums==null){
  6. return result;
  7. }
  8. Arrays.sort(nums);
  9. backtrack(0,nums,result,arr);
  10. return result;
  11. }
  12. private void backtrack(int i,int[] nums,List<List<Integer>> result,List<Integer> arr){
  13. result.add(new ArrayList<>(arr));
  14. for(int j=i;j<nums.length;j++){
  15. if(j>i&&nums[j]==nums[j-1]){
  16. continue;
  17. }
  18. arr.add(nums[j]);
  19. backtrack(j+1,nums,result,arr);
  20. arr.remove(arr.size()-1);
  21. }
  22. }
  23. }

13. 分割字符串使得每个部分都是回文数

131. Palindrome Partitioning (Medium)

Leetcode / 力扣

  1. For example, given s = "aab",
  2. Return
  3. [
  4. ["aa","b"],
  5. ["a","a","b"]
  6. ]
  1. class Solution {
  2. public List<List<String>> partition(String s) {
  3. List<String>arr=new ArrayList<>();
  4. List<List<String>>result=new ArrayList<>();
  5. if(s==null||s.length()==0){
  6. return result;
  7. }
  8. backtrack(arr,result,s);
  9. return result;
  10. }
  11. private void backtrack(List<String> arr,List<List<String>>result,String s){
  12. if(s.length()==0){
  13. result.add(new ArrayList<>(arr));
  14. return;
  15. }
  16. for(int i=0;i<s.length();i++){
  17. if(isPalindrome(s,0,i)){
  18. arr.add(s.substring(0,i+1));
  19. backtrack(arr,result,s.substring(i+1));
  20. arr.remove(arr.size()-1);
  21. }
  22. }
  23. }
  24. private boolean isPalindrome(String s,int begin,int end){
  25. while(begin<end){
  26. if(s.charAt(begin++)!=s.charAt(end--)){
  27. return false;
  28. }
  29. }
  30. return true;
  31. }
  32. }

14. 数独

37. Sudoku Solver (Hard)

Leetcode / 力扣

  1. class Solution {
  2. //回溯算法
  3. public boolean solveSudoku(char[][] board) {
  4. return backTrace(board, 0, 0);
  5. }
  6. //注意这里的参数,row表示第几行,col表示第几列。
  7. private boolean backTrace(char[][] board, int row, int col) {
  8. //注意row是从0开始的,当row等于board.length的时候表示数独的
  9. //最后一行全部读遍历完了,说明数独中的值是有效的,直接返回true
  10. if (row == board.length)
  11. return true;
  12. //如果当前行的最后一列也遍历完了,就从下一行的第一列开始。这里的遍历
  13. //顺序是从第1行的第1列一直到最后一列,然后第二行的第一列一直到最后
  14. //一列,然后第三行的……
  15. if (col == board.length)
  16. return backTrace(board, row + 1, 0);
  17. //如果当前位置已经有数字了,就不能再填了,直接到这一行的下一列
  18. if (board[row][col] != '.')
  19. return backTrace(board, row, col + 1);
  20. //如果上面条件都不满足,我们就从1到9种选择一个合适的数字填入到数独中
  21. for (char i = '1'; i <= '9'; i++) {
  22. //判断当前位置[row,col]是否可以放数字i,如果不能放再判断下
  23. //一个能不能放,直到找到能放的为止,如果从1-9都不能放,就会下面
  24. //直接return false
  25. if (!isValid(board, row, col, i))
  26. continue;
  27. //如果能放数字i,就把数字i放进去
  28. board[row][col] = i;
  29. //如果成功就直接返回,不需要再尝试了
  30. if (backTrace(board, row, col))
  31. return true;
  32. //否则就撤销重新选择
  33. board[row][col] = '.';
  34. }
  35. //如果当前位置[row,col]不能放任何数字,直接返回false
  36. return false;
  37. }
  38. //验证当前位置[row,col]是否可以存放字符c
  39. private static boolean isValid(char[][] board, int row, int col, char c) {
  40. for (int i = 0; i < 9; i++) {
  41. //当前列有没有和字符c重复的
  42. if (board[i][col] == c)
  43. return false;
  44. //当前行有没有和字符c重复的
  45. if (board[row][i] == c)
  46. return false;
  47. //当前的单元格内是否有和字符c重复的
  48. if (board[3 * (row / 3) + i / 3][3 * (col / 3) + i % 3] == c)
  49. return false;
  50. }
  51. return true;
  52. }
  53. }

15. N 皇后

51. N-Queens (Hard)

Leetcode / 力扣

  1. public List<List<String>> solveNQueens(int n) {
  2. char[][] chess = new char[n][n];
  3. for (int i = 0; i < n; i++)
  4. for (int j = 0; j < n; j++)
  5. chess[i][j] = '.';
  6. List<List<String>> res = new ArrayList<>();
  7. solve(res, chess, 0);
  8. return res;
  9. }
  10. private void solve(List<List<String>> res, char[][] chess, int row) {
  11. if (row == chess.length) {
  12. res.add(construct(chess));
  13. return;
  14. }
  15. for (int col = 0; col < chess.length; col++) {
  16. if (valid(chess, row, col)) {
  17. chess[row][col] = 'Q';
  18. solve(res, chess, row + 1);
  19. chess[row][col] = '.';
  20. }
  21. }
  22. }
  23. //row表示第几行,col表示第几列
  24. private boolean valid(char[][] chess, int row, int col) {
  25. //判断当前列有没有皇后,因为他是一行一行往下走的,
  26. //我们只需要检查走过的行数即可,通俗一点就是判断当前
  27. //坐标位置的上面有没有皇后
  28. for (int i = 0; i < row; i++) {
  29. if (chess[i][col] == 'Q') {
  30. return false;
  31. }
  32. }
  33. //判断当前坐标的右上角有没有皇后
  34. for (int i = row - 1, j = col + 1; i >= 0 && j < chess.length; i--, j++) {
  35. if (chess[i][j] == 'Q') {
  36. return false;
  37. }
  38. }
  39. //判断当前坐标的左上角有没有皇后
  40. for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) {
  41. if (chess[i][j] == 'Q') {
  42. return false;
  43. }
  44. }
  45. return true;
  46. }
  47. //把数组转为list
  48. private List<String> construct(char[][] chess) {
  49. List<String> path = new ArrayList<>();
  50. for (int i = 0; i < chess.length; i++) {
  51. path.add(new String(chess[i]));
  52. }
  53. return path;
  54. }

Leedcode算法专题训练(搜索)的更多相关文章

  1. 算法专题训练 搜索a-T3 Ni骑士(ni)

    搞了半天八数码弄不出来就只好来打题解  这道题是在搜索a碰到的(链接: http://pan.baidu.com/s/1jG9rQsQ ) 感觉题目最大亮点就是这英文简写"ni", ...

  2. Leedcode算法专题训练(树)

    递归 一棵树要么是空树,要么有两个指针,每个指针指向一棵树.树是一种递归结构,很多树的问题可以使用递归来处理. 1. 树的高度 104. Maximum Depth of Binary Tree (E ...

  3. Leedcode算法专题训练(分治法)

    归并排序就是一个用分治法的经典例子,这里我用它来举例描述一下上面的步骤: 1.归并排序首先把原问题拆分成2个规模更小的子问题. 2.递归地求解子问题,当子问题规模足够小时,可以一下子解决它.在这个例子 ...

  4. Leedcode算法专题训练(二分查找)

    二分查找实现 非常详细的解释,简单但是细节很重要 https://www.cnblogs.com/kyoner/p/11080078.html 正常实现 Input : [1,2,3,4,5] key ...

  5. Leedcode算法专题训练(排序)

    排序 快速排序 用于求解 Kth Element 问题,也就是第 K 个元素的问题. 可以使用快速排序的 partition() 进行实现.需要先打乱数组,否则最坏情况下时间复杂度为 O(N2). 堆 ...

  6. Leedcode算法专题训练(贪心)

    1. 分配饼干 455. 分发饼干 题目描述:每个孩子都有一个满足度 grid,每个饼干都有一个大小 size,只有饼干的大小大于等于一个孩子的满足度,该孩子才会获得满足.求解最多可以获得满足的孩子数 ...

  7. Leedcode算法专题训练(双指针)

    算法思想 双指针 167. 两数之和 II - 输入有序数组 双指针的典型用法 如果两个指针指向元素的和 sum == target,那么得到要求的结果: 如果 sum > target,移动较 ...

  8. Leedcode算法专题训练(位运算)

    https://www.cnblogs.com/findbetterme/p/10787118.html 看这个就完事了 1. 统计两个数的二进制表示有多少位不同 461. Hamming Dista ...

  9. Leedcode算法专题训练(数组与矩阵)

    1. 把数组中的 0 移到末尾 283. Move Zeroes (Easy) Leetcode / 力扣 class Solution { public void moveZeroes(int[] ...

随机推荐

  1. NGK数字增益平台中如何分配代币产出

    最近很多朋友听说NGK公链的主网和数字增益平台即将上线以后都纷纷表示非常感兴趣,已经基本了解了NGK代币的产出方式,但还是对代币产出分配的问题不是很明确.今天小编就给大家科普一下,NGK代币在NGK数 ...

  2. jetty的jndi

    jetty的jndi和tomcat的用法 tomcat的jndi是内置的,在web.xml文件里直接默认支持的,所有web项目可以直接使用 <resources> <!-- < ...

  3. winform导出excel

    public void AllDataSetToExcel(DataSet ds) { string saveFileName = ""; bool fileSaved = fal ...

  4. 页面导入导出EXCEL

    引用 using Microsoft.Office.Interop.Excel;using System.Reflection;//反射命名空间using System.IO; protected v ...

  5. ElasticSearcher的安装以及安装过程中出现的问题

    先给出参考链接,带安装成功后再进行总结整个过程. 参考链接:https://blog.csdn.net/fjyab/article/details/81101284 java操作ElasticSear ...

  6. 基于【腾讯云函数】/【GitHub Actions】/【Docker】的每日签到脚本(支持多账号使用)

    每日签到集合 基于[腾讯云函数]/[GitHub Actions]/[Docker]的每日签到脚本 支持多账号使用 特别声明: 本仓库发布的脚本及其中涉及的任何解锁和解密分析脚本,仅用于测试和学习研究 ...

  7. Vue3.0+Electron聊天室|electron跨平台仿QQ客户端|vue3.x聊天应用

    基于vue3+electron11跨端仿制QQ桌面应用实战Vue3ElectronQchat. 使用vue3+electron+vuex4+ant-design-vue+v3scroll+v3laye ...

  8. Google单元测试框架gtest之官方sample笔记2--类型参数测试

    gtest 提供了类型参数化测试方案,可以测试不同类型的数据接口,比如模板测试.可以定义参数类型列表,按照列表定义的类型,每个测试case都执行一遍. 本例中,定义了2种计算素数的类,一个是实时计算, ...

  9. RabbitMQ(三) SpringBoot2.x 集成 RabbitMQ

    3-1 RabbitMQ 整合 SpringBoot2.x 生产者发送消息 创建 SpringBoot 项目application.properties 配置 spring.rabbitmq.host ...

  10. Java并发之ThreadPoolExecutor源码解析(三)

    Worker 先前,笔者讲解到ThreadPoolExecutor.addWorker(Runnable firstTask, boolean core),在这个方法中工作线程可能创建成功,也可能创建 ...