332.重新安排行程
题目链接:https://leetcode.cn/problems/reconstruct-itinerary/
文档讲解:https://programmercarl.com/0332.%E9%87%8D%E6%96%B0%E5%AE%89%E6%8E%92%E8%A1%8C%E7%A8%8B.html
视频讲解:https://www.bilibili.com/video/BV1cy4y167mM/
思路
- 选择合适的容器:选择Map来存储目的地信息,并且用一个Integer来存储机票的次数,当使用对应机票,就将次数-1,防止出现死循环。
Map<String, Map<String, Integer>> map = new HashMap<String, Map<String, Integer>>();
- 回溯的返回值:本题的返回值是
boolean
,和之前的void不同。因为本题只用找到一条合适的路线就可以返回了,不像之前的题目要搜索所有的排列组合。
代码
class Solution {
Map<String, Map<String, Integer>> map;
Deque<String> res;
public List<String> findItinerary(List<List<String>> tickets) {
map = new HashMap<String, Map<String, Integer>>();
res = new LinkedList<>();
for (List<String> t : tickets) {
Map<String, Integer> temp; //记录一个终点以及次数
if (map.containsKey(t.get(0))) { // 如果map中存在t中的起点,就把终点及其对应的次数+1
temp = map.get(t.get(0));
temp.put(t.get(1), temp.getOrDefault(t.get(1), 0) + 1);
} else { // 如果map中不存在t的起点,就在map中存入起点,终点和次数1
temp = new TreeMap<>(); // 升序map
temp.put(t.get(1), 1);
}
map.put(t.get(0), temp);
}
res.add("JFK");
backtracking(tickets.size());
return new ArrayList<>(res);
}
public boolean backtracking(int ticketNum) {
if (res.size() == ticketNum + 1) return true;
String last = res.getLast();
if (map.containsKey(last)) { // 防止出现null
for (Map.Entry<String, Integer> target : map.get(last).entrySet()) {
int count = target.getValue();
if (count > 0) {
res.add(target.getKey());
target.setValue(count - 1);
if (backtracking(ticketNum)) return true; // 如果下面传上来的是true,说明有了结果,就也返回true,不继续回溯
res.removeLast();
target.setValue(count);
}
}
}
return false;
}
}
51. N皇后
题目链接:https://leetcode.cn/problems/n-queens/
文档讲解:https://programmercarl.com/0051.N%E7%9A%87%E5%90%8E.html
视频讲解:https://www.bilibili.com/video/BV1Rd4y1c7Bq/
思路
- 二维矩阵中矩阵的高就是这棵树的高度,矩阵的宽就是树形结构中每一个节点的宽度。那么我们用皇后们的约束条件,来回溯搜索这棵树,只要搜索到了树的叶子节点,说明就找到了皇后们的合理位置了。
- 递归函数参数
定义全局变量二维列表res
来记录最终结果。参数n是棋盘的大小,然后用row来记录当前遍历到棋盘的第几层了。
List<List<String>> res = new ArrayList<>();
public void backtracking(char[][] chessBoard, int n, int row) {
- 递归终止条件
当递归到棋盘最底层(也就是叶子节点)的时候,就可以收集结果并返回了。
if (row == n) {
res.add(ArraytoList(chessBoard));
return;
}
- 单层搜索的逻辑
递归深度就是row控制棋盘的行,每一层里for循环的i控制棋盘的列,一行一列,确定了放置皇后的位置。每次都是要从新的一行的起始位置开始搜,所以都是从0开始。
for (int i = 0; i < n; i++) {
if (isValid(row, i, n, chessBoard)) {
chessBoard[row][i] = 'Q';
backtracking(chessBoard, n, row + 1);
chessBoard[row][i] = '.';
}
}
代码
class Solution {
List<List<String>> res = new ArrayList<>();
public List<List<String>> solveNQueens(int n) {
char[][] chessBoard = new char[n][n];
for (char[] c : chessBoard) {
Arrays.fill(c, '.');
}
backtracking(chessBoard, n, 0);
return res;
}
public void backtracking(char[][] chessBoard, int n, int row) {
if (row == n) {
res.add(ArraytoList(chessBoard));
return;
}
for (int i = 0; i < n; i++) {
if (isValid(row, i, n, chessBoard)) {
chessBoard[row][i] = 'Q';
backtracking(chessBoard, n, row + 1);
chessBoard[row][i] = '.';
}
}
}
public boolean isValid(int row, int col, int n, char[][] chessBoard) {
for (int i = 0; i < row; i++) { // 检查列
if (chessBoard[i][col] == 'Q') return false;
}
for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) { // 检查45°
if (chessBoard[i][j] == 'Q') return false;
}
for (int i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) { // 检查135°
if (chessBoard[i][j] == 'Q') return false;
}
return true;
}
public List<String> ArraytoList(char[][] chessBoard) {
List<String> list = new ArrayList<>();
for (char[] c : chessBoard) {
list.add(String.copyValueOf(c));
}
return list;
}
}
37. 解数独
题目链接:https://leetcode.cn/problems/sudoku-solver/
文档讲解:https://programmercarl.com/0037.%E8%A7%A3%E6%95%B0%E7%8B%AC.html
视频讲解:https://www.bilibili.com/video/BV1TW4y1471V/
思路
- 本题是二维递归,需要两个循环加一个递归,两个循环来确定一个格子,递归来判断格子中是否能放1-9。
- 递归的返回值是boolean,找到一种解法就可以返回了。
代码
class Solution {
public void solveSudoku(char[][] board) {
backtracking(board);
}
public boolean backtracking(char[][] board) {
for (int i = 0; i < board.length; i++) {
for (int j = 0; j < board[0].length; j++) {
if (board[i][j] == '.') {
for (char k = '1'; k <= '9'; k++) {
if (isValid(i, j, k, board)) {
board[i][j] = k;
if (backtracking(board)) return true; // 如果找到合适一组立刻返回
board[i][j] = '.';
}
}
return false; // 9个数都试完了,都不行,那么就返回false
}
}
}
return true; // 遍历完没有返回false,说明找到了合适棋盘位置了
}
public boolean isValid(int row, int col, char c, char[][] board) {
for (int i = 0; i < board.length; i++) {
if (i != row && board[i][col] == c) return false;
if (i != col && board[row][i] == c) return false;
}
for (int i = (row / 3) * 3; i < (row / 3) * 3 + 3; i++) {
for (int j = (col / 3) * 3; j < (col / 3) * 3 + 3; j++) {
if (i != row && j != col && board[i][j] == c) return false;
}
}
return true;
}
}
回溯部分总结
文档讲解:https://programmercarl.com/%E5%9B%9E%E6%BA%AF%E6%80%BB%E7%BB%93.html