1.分治
1.1 【中等】Pow(x, n)(50)

递归
/*** 建议背这个*/public class MyPow {public double myPow(double x, int n) {long N = n;// 根据N是否大于等于0进行分治return N >= 0 ? quickMul(x, N) : 1.0 / quickMul(x, -N);}public double quickMul(double x, long N) {if (N == 0) {return 1.0;}double y = quickMul(x, N / 2);// 偶数直接/2平方即可,否则还要乘以xreturn N % 2 == 0 ? y * y : y * y * x;}}
非递归
public class MyPow {public double myPow(double x, int n) {long N = n;return N >= 0 ? quickMul(x, N) : 1.0 / quickMul(x, -N);}dfpublic double quickMul(double x, long N) {double ans = 1.0;// 贡献的初始值为 xdouble xContribute = x;// 在对 N 进行二进制拆分的同时计算答案while (N > 0) {if (N % 2 == 1) {// 如果 N 二进制表示的最低位为 1,那么需要计入贡献ans *= xContribute;}// 将贡献不断地平方xContribute *= xContribute;// 舍弃 N 二进制表示的最低位,这样我们每次只要判断最低位即可N /= 2;}return ans;}}
2.回溯
2.1 【中等】子集(78)

/*** 回溯就是要递归,递归一次后移除元素,再进行一次递归*/public class Subsets {List<Integer> t = new ArrayList<>();List<List<Integer>> ans = new ArrayList<>();public List<List<Integer>> subsets(int[] nums) {dfs(0, nums);return ans;}public void dfs(int cur, int[] nums) {if (cur == nums.length) {ans.add(new ArrayList<>(t));return;}t.add(nums[cur]);dfs(cur + 1, nums);t.remove(t.size() - 1);dfs(cur + 1, nums);}}
2.2 【中等】电话号码的字母组合(17)

/*** 这个回溯在删除元素后不需要再进行一次递归,实战时可以都尝试一下*/class Solution {public List<String> letterCombinations(String digits) {List<String> combinations = new ArrayList<>();if (digits.length() == 0) {return combinations;}// 需要构建一个mapMap<Character, String> phoneMap = new HashMap<Character, String>() {{put('2', "abc");put('3', "def");put('4', "ghi");put('5', "jkl");put('6', "mno");put('7', "pqrs");put('8', "tuv");put('9', "wxyz");}};backtrack(combinations, phoneMap, digits, 0, new StringBuffer());return combinations;}public void backtrack(List<String> combinations,Map<Character, String> phoneMap,String digits,Integer index,StringBuffer combination){if(index==digits.length()){combinations.add(combination.toString());}else{char digit = digits.charAt(index);String letters = phoneMap.get(digit);int lettersCount = letters.length();for (int i = 0; i < lettersCount; i++) {combination.append(letters.charAt(i));backtrack(combinations,phoneMap,digits,index+1,combination);// 递归后删除元素combination.deleteCharAt(index);}}}}
2.3 【困难】N 皇后(51)

/*** 需要找出两条斜线与行列的关系,这边的话直接死记就行*/public class SolveNQueens {public List<List<String>> solveNQueens(int n) {List<List<String>> solutions = new ArrayList<>();int[] queens = new int[n];Arrays.fill(queens, -1);Set<Integer> columns = new HashSet<>();Set<Integer> diagonals1 = new HashSet<>();Set<Integer> diagonals2 = new HashSet<>();backtrack(solutions, queens, n, 0, columns, diagonals1, diagonals2);return solutions;}public void backtrack(List<List<String>> solutions, int[] queens, int n, int row, Set<Integer> columns, Set<Integer> diagonals1, Set<Integer> diagonals2) {if (row == n) {List<String> board = generateBoard(queens, n);solutions.add(board);} else {for (int i = 0; i < n; i++) {if (columns.contains(i)) {continue;}int diagonal1 = row - i;if (diagonals1.contains(diagonal1)) {continue;}int diagonal2 = row + i;if (diagonals2.contains(diagonal2)) {continue;}queens[row] = i;columns.add(i);diagonals1.add(diagonal1);diagonals2.add(diagonal2);backtrack(solutions, queens, n, row + 1, columns, diagonals1, diagonals2);queens[row] = -1;columns.remove(i);diagonals1.remove(diagonal1);diagonals2.remove(diagonal2);}}}public List<String> generateBoard(int[] queens, int n) {List<String> board = new ArrayList<>();for (int i = 0; i < n; i++) {char[] row = new char[n];Arrays.fill(row, '.');row[queens[i]] = 'Q';board.add(new String(row));}return board;}}
2.4 【中等】迷宫问题(HJ43)


本题不好用动态规划,因为它可以上下左右移动,所以深搜是最好的,当i=row-1,j=column-1退出循环
public class MinPath {/*** 所有可能路径*/private static Stack<int[]> PATH = new Stack<>();/*** 最短路径*/private static Stack<int[]> MIN_PATH = new Stack<>();public static void main(String[] args) {Scanner sc = new Scanner(System.in);int row = sc.nextInt();int column = sc.nextInt();int[][] maze = new int[row][column];for (int i = 0; i < row; i++) {for (int j = 0; j < column; j++) {maze[i][j] = sc.nextInt();}}dfs(maze, 0, 0, row, column);while (!MIN_PATH.isEmpty()) {int[] ints = MIN_PATH.pop();System.out.println("(" + ints[0] + "," + ints[1] + ")");}}private static void dfs(int[][] maze, int i, int j, int row, int column) {if (i < 0 || i >= row || j < 0 || j >= column || maze[i][j] == 1) {return;}PATH.push(new int[]{i, j});if (i == row - 1 && j == column - 1) {if (MIN_PATH.isEmpty() || PATH.size() < MIN_PATH.size()) {for (int k = PATH.size() - 1; k >= 0; k--) {MIN_PATH.push(PATH.get(k));}}}maze[i][j] = 1;dfs(maze, i - 1, j, row, column);dfs(maze, i + 1, j, row, column);dfs(maze, i, j - 1, row, column);dfs(maze, i, j + 1, row, column);maze[i][j] = 0;PATH.pop();}}
2.5 【中等】全排列(46)

/*** 需要交换位置,建议死记*/public class Permute {List<List<Integer>> result = new ArrayList<>();public List<List<Integer>> permute(int[] nums) {List<Integer> list = new ArrayList<>();for (int num : nums) {list.add(num);}dfs(nums, 0, list);return result;}public void dfs(int[] nums, int first, List<Integer> cur) {if (first == nums.length) {result.add(new ArrayList<>(cur));return;}for (int i = first; i < nums.length; i++) {// 动态维护数组Collections.swap(cur, first, i);// 继续递归填下一个数dfs(nums, first + 1, cur);// 撤销操作Collections.swap(cur, first, i);}}}
2.6 【困难】24 点游戏(679)

class Solution {//回溯枚举 + 部分剪枝优化static final int TARGET = 24;//误差static final double EPSILON = 1e-6;//加、乘、减、除static final int ADD = 0, MULTIPLY = 1, SUBTRACT = 2, DIVIDE = 3;public boolean judgePoint24(int[] nums) {//4个数入listList<Double> list = new ArrayList<Double>();for (int num : nums) {list.add((double) num);}return solve(list);}public boolean solve(List<Double> list) {if (list.size() == 0) {return false;}//算完后只剩一个数,即结果,误差在可接受范围内判trueif (list.size() == 1) {return Math.abs(list.get(0) - TARGET) < EPSILON;}int size = list.size();//list里挑出一个数for (int i = 0; i < size; i++) {//再挑一个,做运算for (int j = 0; j < size; j++) {//不能挑一样的if (i != j) {//没被挑到的扔进list2List<Double> list2 = new ArrayList<Double>();for (int k = 0; k < size; k++) {if (k != i && k != j) {list2.add(list.get(k));}}//四种运算挑一个,算完的结果加到list2里做第三个数for (int k = 0; k < 4; k++) {//k < 2是指,所挑运算为加或乘//i > j是为了判定交换律的成立,如果i < j,说明是第一次做加或乘运算//eg: i = 0, j = 1, k = 1 → 挑第一个和第二个数,两数相乘// i = 1, j = 0, k = 1 → 挑第二个和第一个数,两数相乘 → 无效的重复计算if (k < 2 && i > j) {continue;}//加if (k == ADD) {list2.add(list.get(i) + list.get(j));//乘} else if (k == MULTIPLY) {list2.add(list.get(i) * list.get(j));//减} else if (k == SUBTRACT) {list2.add(list.get(i) - list.get(j));//除} else if (k == DIVIDE) {//如果除数等于0,直接判这次所挑选的运算为false,跳出此次循环if (Math.abs(list.get(j)) < EPSILON) {continue;} else {list2.add(list.get(i) / list.get(j));}}//至此已经由4→3,进入下一层由3→2的过程,依次类推if (solve(list2)) {return true;}//没凑成24就把加到list2末尾的结果数删掉,考虑下种运算可能list2.remove(list2.size() - 1);}}}}return false;}}
