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平方即可,否则还要乘以x
return 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);
}df
public double quickMul(double x, long N) {
double ans = 1.0;
// 贡献的初始值为 x
double 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;
}
// 需要构建一个map
Map<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个数入list
List<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;
}
//算完后只剩一个数,即结果,误差在可接受范围内判true
if (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) {
//没被挑到的扔进list2
List<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;
}
}