1.分治

1.1 【中等】Pow(x, n)(50)

image.png
递归

  1. /**
  2. * 建议背这个
  3. */
  4. public class MyPow {
  5. public double myPow(double x, int n) {
  6. long N = n;
  7. // 根据N是否大于等于0进行分治
  8. return N >= 0 ? quickMul(x, N) : 1.0 / quickMul(x, -N);
  9. }
  10. public double quickMul(double x, long N) {
  11. if (N == 0) {
  12. return 1.0;
  13. }
  14. double y = quickMul(x, N / 2);
  15. // 偶数直接/2平方即可,否则还要乘以x
  16. return N % 2 == 0 ? y * y : y * y * x;
  17. }
  18. }

非递归

  1. public class MyPow {
  2. public double myPow(double x, int n) {
  3. long N = n;
  4. return N >= 0 ? quickMul(x, N) : 1.0 / quickMul(x, -N);
  5. }df
  6. public double quickMul(double x, long N) {
  7. double ans = 1.0;
  8. // 贡献的初始值为 x
  9. double xContribute = x;
  10. // 在对 N 进行二进制拆分的同时计算答案
  11. while (N > 0) {
  12. if (N % 2 == 1) {
  13. // 如果 N 二进制表示的最低位为 1,那么需要计入贡献
  14. ans *= xContribute;
  15. }
  16. // 将贡献不断地平方
  17. xContribute *= xContribute;
  18. // 舍弃 N 二进制表示的最低位,这样我们每次只要判断最低位即可
  19. N /= 2;
  20. }
  21. return ans;
  22. }
  23. }

2.回溯

2.1 【中等】子集(78)

image.png

  1. /**
  2. * 回溯就是要递归,递归一次后移除元素,再进行一次递归
  3. */
  4. public class Subsets {
  5. List<Integer> t = new ArrayList<>();
  6. List<List<Integer>> ans = new ArrayList<>();
  7. public List<List<Integer>> subsets(int[] nums) {
  8. dfs(0, nums);
  9. return ans;
  10. }
  11. public void dfs(int cur, int[] nums) {
  12. if (cur == nums.length) {
  13. ans.add(new ArrayList<>(t));
  14. return;
  15. }
  16. t.add(nums[cur]);
  17. dfs(cur + 1, nums);
  18. t.remove(t.size() - 1);
  19. dfs(cur + 1, nums);
  20. }
  21. }

2.2 【中等】电话号码的字母组合(17)

image.png

  1. /**
  2. * 这个回溯在删除元素后不需要再进行一次递归,实战时可以都尝试一下
  3. */
  4. class Solution {
  5. public List<String> letterCombinations(String digits) {
  6. List<String> combinations = new ArrayList<>();
  7. if (digits.length() == 0) {
  8. return combinations;
  9. }
  10. // 需要构建一个map
  11. Map<Character, String> phoneMap = new HashMap<Character, String>() {{
  12. put('2', "abc");
  13. put('3', "def");
  14. put('4', "ghi");
  15. put('5', "jkl");
  16. put('6', "mno");
  17. put('7', "pqrs");
  18. put('8', "tuv");
  19. put('9', "wxyz");
  20. }};
  21. backtrack(combinations, phoneMap, digits, 0, new StringBuffer());
  22. return combinations;
  23. }
  24. public void backtrack(List<String> combinations,Map<Character, String> phoneMap,String digits,Integer index,StringBuffer combination){
  25. if(index==digits.length()){
  26. combinations.add(combination.toString());
  27. }else{
  28. char digit = digits.charAt(index);
  29. String letters = phoneMap.get(digit);
  30. int lettersCount = letters.length();
  31. for (int i = 0; i < lettersCount; i++) {
  32. combination.append(letters.charAt(i));
  33. backtrack(combinations,phoneMap,digits,index+1,combination);
  34. // 递归后删除元素
  35. combination.deleteCharAt(index);
  36. }
  37. }
  38. }
  39. }

2.3 【困难】N 皇后(51)

image.png

  1. /**
  2. * 需要找出两条斜线与行列的关系,这边的话直接死记就行
  3. */
  4. public class SolveNQueens {
  5. public List<List<String>> solveNQueens(int n) {
  6. List<List<String>> solutions = new ArrayList<>();
  7. int[] queens = new int[n];
  8. Arrays.fill(queens, -1);
  9. Set<Integer> columns = new HashSet<>();
  10. Set<Integer> diagonals1 = new HashSet<>();
  11. Set<Integer> diagonals2 = new HashSet<>();
  12. backtrack(solutions, queens, n, 0, columns, diagonals1, diagonals2);
  13. return solutions;
  14. }
  15. public void backtrack(List<List<String>> solutions, int[] queens, int n, int row, Set<Integer> columns, Set<Integer> diagonals1, Set<Integer> diagonals2) {
  16. if (row == n) {
  17. List<String> board = generateBoard(queens, n);
  18. solutions.add(board);
  19. } else {
  20. for (int i = 0; i < n; i++) {
  21. if (columns.contains(i)) {
  22. continue;
  23. }
  24. int diagonal1 = row - i;
  25. if (diagonals1.contains(diagonal1)) {
  26. continue;
  27. }
  28. int diagonal2 = row + i;
  29. if (diagonals2.contains(diagonal2)) {
  30. continue;
  31. }
  32. queens[row] = i;
  33. columns.add(i);
  34. diagonals1.add(diagonal1);
  35. diagonals2.add(diagonal2);
  36. backtrack(solutions, queens, n, row + 1, columns, diagonals1, diagonals2);
  37. queens[row] = -1;
  38. columns.remove(i);
  39. diagonals1.remove(diagonal1);
  40. diagonals2.remove(diagonal2);
  41. }
  42. }
  43. }
  44. public List<String> generateBoard(int[] queens, int n) {
  45. List<String> board = new ArrayList<>();
  46. for (int i = 0; i < n; i++) {
  47. char[] row = new char[n];
  48. Arrays.fill(row, '.');
  49. row[queens[i]] = 'Q';
  50. board.add(new String(row));
  51. }
  52. return board;
  53. }
  54. }

2.4 【中等】迷宫问题(HJ43)

image.png
image.png
本题不好用动态规划,因为它可以上下左右移动,所以深搜是最好的,当i=row-1,j=column-1退出循环

  1. public class MinPath {
  2. /**
  3. * 所有可能路径
  4. */
  5. private static Stack<int[]> PATH = new Stack<>();
  6. /**
  7. * 最短路径
  8. */
  9. private static Stack<int[]> MIN_PATH = new Stack<>();
  10. public static void main(String[] args) {
  11. Scanner sc = new Scanner(System.in);
  12. int row = sc.nextInt();
  13. int column = sc.nextInt();
  14. int[][] maze = new int[row][column];
  15. for (int i = 0; i < row; i++) {
  16. for (int j = 0; j < column; j++) {
  17. maze[i][j] = sc.nextInt();
  18. }
  19. }
  20. dfs(maze, 0, 0, row, column);
  21. while (!MIN_PATH.isEmpty()) {
  22. int[] ints = MIN_PATH.pop();
  23. System.out.println("(" + ints[0] + "," + ints[1] + ")");
  24. }
  25. }
  26. private static void dfs(int[][] maze, int i, int j, int row, int column) {
  27. if (i < 0 || i >= row || j < 0 || j >= column || maze[i][j] == 1) {
  28. return;
  29. }
  30. PATH.push(new int[]{i, j});
  31. if (i == row - 1 && j == column - 1) {
  32. if (MIN_PATH.isEmpty() || PATH.size() < MIN_PATH.size()) {
  33. for (int k = PATH.size() - 1; k >= 0; k--) {
  34. MIN_PATH.push(PATH.get(k));
  35. }
  36. }
  37. }
  38. maze[i][j] = 1;
  39. dfs(maze, i - 1, j, row, column);
  40. dfs(maze, i + 1, j, row, column);
  41. dfs(maze, i, j - 1, row, column);
  42. dfs(maze, i, j + 1, row, column);
  43. maze[i][j] = 0;
  44. PATH.pop();
  45. }
  46. }

2.5 【中等】全排列(46)

image.png

  1. /**
  2. * 需要交换位置,建议死记
  3. */
  4. public class Permute {
  5. List<List<Integer>> result = new ArrayList<>();
  6. public List<List<Integer>> permute(int[] nums) {
  7. List<Integer> list = new ArrayList<>();
  8. for (int num : nums) {
  9. list.add(num);
  10. }
  11. dfs(nums, 0, list);
  12. return result;
  13. }
  14. public void dfs(int[] nums, int first, List<Integer> cur) {
  15. if (first == nums.length) {
  16. result.add(new ArrayList<>(cur));
  17. return;
  18. }
  19. for (int i = first; i < nums.length; i++) {
  20. // 动态维护数组
  21. Collections.swap(cur, first, i);
  22. // 继续递归填下一个数
  23. dfs(nums, first + 1, cur);
  24. // 撤销操作
  25. Collections.swap(cur, first, i);
  26. }
  27. }
  28. }

2.6 【困难】24 点游戏(679)

image.png

  1. class Solution {
  2. //回溯枚举 + 部分剪枝优化
  3. static final int TARGET = 24;
  4. //误差
  5. static final double EPSILON = 1e-6;
  6. //加、乘、减、除
  7. static final int ADD = 0, MULTIPLY = 1, SUBTRACT = 2, DIVIDE = 3;
  8. public boolean judgePoint24(int[] nums) {
  9. //4个数入list
  10. List<Double> list = new ArrayList<Double>();
  11. for (int num : nums) {
  12. list.add((double) num);
  13. }
  14. return solve(list);
  15. }
  16. public boolean solve(List<Double> list) {
  17. if (list.size() == 0) {
  18. return false;
  19. }
  20. //算完后只剩一个数,即结果,误差在可接受范围内判true
  21. if (list.size() == 1) {
  22. return Math.abs(list.get(0) - TARGET) < EPSILON;
  23. }
  24. int size = list.size();
  25. //list里挑出一个数
  26. for (int i = 0; i < size; i++) {
  27. //再挑一个,做运算
  28. for (int j = 0; j < size; j++) {
  29. //不能挑一样的
  30. if (i != j) {
  31. //没被挑到的扔进list2
  32. List<Double> list2 = new ArrayList<Double>();
  33. for (int k = 0; k < size; k++) {
  34. if (k != i && k != j) {
  35. list2.add(list.get(k));
  36. }
  37. }
  38. //四种运算挑一个,算完的结果加到list2里做第三个数
  39. for (int k = 0; k < 4; k++) {
  40. //k < 2是指,所挑运算为加或乘
  41. //i > j是为了判定交换律的成立,如果i < j,说明是第一次做加或乘运算
  42. //eg: i = 0, j = 1, k = 1 → 挑第一个和第二个数,两数相乘
  43. // i = 1, j = 0, k = 1 → 挑第二个和第一个数,两数相乘 → 无效的重复计算
  44. if (k < 2 && i > j) {
  45. continue;
  46. }
  47. //加
  48. if (k == ADD) {
  49. list2.add(list.get(i) + list.get(j));
  50. //乘
  51. } else if (k == MULTIPLY) {
  52. list2.add(list.get(i) * list.get(j));
  53. //减
  54. } else if (k == SUBTRACT) {
  55. list2.add(list.get(i) - list.get(j));
  56. //除
  57. } else if (k == DIVIDE) {
  58. //如果除数等于0,直接判这次所挑选的运算为false,跳出此次循环
  59. if (Math.abs(list.get(j)) < EPSILON) {
  60. continue;
  61. } else {
  62. list2.add(list.get(i) / list.get(j));
  63. }
  64. }
  65. //至此已经由4→3,进入下一层由3→2的过程,依次类推
  66. if (solve(list2)) {
  67. return true;
  68. }
  69. //没凑成24就把加到list2末尾的结果数删掉,考虑下种运算可能
  70. list2.remove(list2.size() - 1);
  71. }
  72. }
  73. }
  74. }
  75. return false;
  76. }
  77. }