37.解数独
思路:回溯法。首先遍历数组,保存其中已有的数字信息,这些是不可变的。同时记录需要填写的格子。之后每次从需要填写的格子中拿出一个,尝试填写与现有其他格子不冲突的数字,更新状态,并递归尝试填写下一个格子。当所有格子填写完成时,则找到解。若填写下一个格子时,所有数字均冲突,则说明当前格子填写当前值时无解,继续寻找下一个可能的解。
代码:
private boolean[][] rows = new boolean[9][9];
private boolean[][] cols = new boolean[9][9];
private boolean[][][] boxes = new boolean[3][3][9];
private List<int[]> empty = new ArrayList<>();
public void solveSudoku(char[][] board) {
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
if (board[i][j] == '.') {
empty.add(new int[]{i, j});
} else {
rows[i][board[i][j] - '1'] = true;
cols[board[i][j] - '1'][j] = true;
boxes[i / 3][j / 3][board[i][j] - '1'] = true;
}
}
}
solve(board, 0);
}
private boolean solve(char[][] board, int index) {
if (index == empty.size()) {
return true;
}
int currentX = empty.get(index)[0], currentY = empty.get(index)[1];
for (int temp = 0; temp < 9; temp++) {
if (!rows[currentX][temp] && !cols[temp][currentY] && !boxes[currentX / 3][currentY / 3][temp]) {
board[currentX][currentY] = (char)('1' + temp);
rows[currentX][temp] = true;
cols[temp][currentY] = true;
boxes[currentX / 3][currentY / 3][temp] = true;
if (solve(board, index + 1)) {
return true;
}
board[currentX][currentY] = '.';
rows[currentX][temp] = false;
cols[temp][currentY] = false;
boxes[currentX / 3][currentY / 3][temp] = false;
}
}
return false;
}
时间复杂度:O(9n),其中n为数独中空格子的数量
空间复杂度:O(n)
38.外观数列
思路:递归求解。
代码:
public String countAndSay(int n) {
if (n == 1) {
return "1";
}
String last = countAndSay(n-1);
StringBuilder sb = new StringBuilder();
char c = last.charAt(0);
int count = 1;
for (int i = 1; i < last.length(); i++) {
if (last.charAt(i) == c) {
count ++;
} else {
sb.append(count);
sb.append(c);
c = last.charAt(i);
count = 1;
}
}
sb.append(count);
sb.append(c);
return sb.toString();
}
时间复杂度:O(n*m),m为1~n中最长外观数列字符串的长度
空间复杂度:O(m)
39.组合总和
思路:回溯法。从index=0开始,考虑放入有可能的个数个candidates[index],然后推进条件。target = 0时,找到一个组合,放入结果中并返回。当index = candidates.length时,说明后续无解,返回。
代码:
public List<List<Integer>> combinationSum(int[] candidates, int target) {
List<List<Integer>> res = new ArrayList<>();
List<Integer> current = new ArrayList<>();
combinationSum(candidates, 0, target, res, current);
return res;
}
private void combinationSum(int[] candidates, int start, int target, List<List<Integer>> res, List<Integer> current) {
if (target == 0) {
res.add(new ArrayList<>(current));
return;
}
if (start == candidates.length) {
return;
}
combinationSum(candidates, start + 1, target, res, current);
int max = target / candidates[start];
for (int i = 0; i < max; i++) {
for (int j = 0; j < i + 1; j++) {
current.add(candidates[start]);
}
combinationSum(candidates, start + 1, target - (i + 1) * candidates[start], res, current);
for (int j = 0; j < i + 1; j++) {
current.remove(current.size() - 1);
}
}
}
时间复杂度:O(S),其中S为所有可行解的元素个数之和
空间复杂度:O(target)