题目链接: 332.重新安排行程
文章讲解: 332.重新安排行程
回溯三部曲
如果单纯的回溯搜索(深搜)并不难,难还难在容器的选择和使用上。
// 回溯 代码随想录方法1 BBQ 超出时间限制了 二刷再看代码随想录的方法2吧
// 超时应该是容器用的不太合适
class Solution {
private LinkedList<String> res;
private LinkedList<String> path = new LinkedList<>();
public List<String> findItinerary(List<List<String>> tickets) {
Collections.sort(tickets, (a, b) -> a.get(1).compareTo(b.get(1)));
path.add("JFK");
boolean[] used = new boolean[tickets.size()];
backTracking((ArrayList) tickets, used);
return res;
}
public boolean backTracking(ArrayList<List<String>> tickets, boolean[] used){
if(path.size() == tickets.size() + 1){
res = new LinkedList(path);
return true;
}
for(int i = 0; i < tickets.size(); i++){
if(!used[i] && tickets.get(i).get(0).equals(path.getLast())){
path.add(tickets.get(i).get(1));
used[i] = true;
if(backTracking(tickets, used)){
return true;
}
used[i] = false;
path.removeLast();
}
}
return false;
}
}
题目链接: 51.N皇后
文章讲解: 51.N皇后
视频讲解: 51.N皇后
棋盘的宽度就是for循环的长度,递归的深度就是棋盘的高度,这样就可以套进回溯法的模板里了。
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(n, 0, chessboard);
return res;
}
public void backTracking(int n, int row, char[][] chessboard){
if(row == n){
res.add(Array2List(chessboard));
return;
}
for(int col = 0; col < n; col++){
if(isValid(row, col, n, chessboard)){
chessboard[row][col] = 'Q';
backTracking(n, row + 1, chessboard);
chessboard[row][col] = '.';
}
}
}
public List Array2List(char[][] chessboard){
List<String> list = new ArrayList<>();
for(char[] c : chessboard){
list.add(String.copyValueOf(c)); // copyValueOf()方法将整个字符数组复制到字符串str中
}
return list;
}
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;
}
}
// 检查45度对角线
for(int i = row-1, j=col-1; i>=0&&j>=0; i--, j--){
if(chessboard[i][j] == 'Q'){
return false;
}
}
// 检查135度对角线
for(int i = row-1, j=col+1; i>=0 && j<=n-1; i--, j++){
if(chessboard[i][j] == 'Q'){
return false;
}
}
return true;
}
}
题目链接: 5. 解数独
文章讲解: 5. 解数独
视频讲解: 5. 解数独
二维递归的思维逻辑
class Solution {
public void solveSudoku(char[][] board) {
solveSudokuHelper(board);
}
private boolean solveSudokuHelper(char[][] board){
//「一个for循环遍历棋盘的行,一个for循环遍历棋盘的列,
// 一行一列确定下来之后,递归遍历这个位置放9个数字的可能性!」
for (int i = 0; i < 9; i++){ // 遍历行
for (int j = 0; j < 9; j++){ // 遍历列
if (board[i][j] != '.'){ // 跳过原始数字
continue;
}
for (char k = '1'; k <= '9'; k++){ // (i, j) 这个位置放k是否合适
if (isValidSudoku(i, j, k, board)){
board[i][j] = k;
if (solveSudokuHelper(board)){ // 如果找到合适一组立刻返回
return true;
}
board[i][j] = '.';
}
}
// 9个数都试完了,都不行,那么就返回false
return false;
// 因为如果一行一列确定下来了,这里尝试了9个数都不行,说明这个棋盘找不到解决数独问题的解!
// 那么会直接返回, 「这也就是为什么没有终止条件也不会永远填不满棋盘而无限递归下去!」
}
}
// 遍历完没有返回false,说明找到了合适棋盘位置了
return true;
}
/**
* 判断棋盘是否合法有如下三个维度:
* 同行是否重复
* 同列是否重复
* 9宫格里是否重复
*/
private boolean isValidSudoku(int row, int col, char val, char[][] board){
// 同行是否重复
for (int i = 0; i < 9; i++){
if (board[row][i] == val){
return false;
}
}
// 同列是否重复
for (int j = 0; j < 9; j++){
if (board[j][col] == val){
return false;
}
}
// 9宫格里是否重复
int startRow = (row / 3) * 3;
int startCol = (col / 3) * 3;
for (int i = startRow; i < startRow + 3; i++){
for (int j = startCol; j < startCol + 3; j++){
if (board[i][j] == val){
return false;
}
}
}
return true;
}
}
今天这三道题都非常难,那么这么难的题,为啥一天做三道?
因为 一刷 也不求大家能把这么难的问题解决,所以 大家一刷的时候,就了解一下题目的要求,了解一下解题思路,不求能直接写出代码,先大概熟悉一下这些题,二刷的时候,随着对回溯算法的深入理解,再去解决如下三题。
大家今天的任务,其实是 对回溯算法章节做一个总结就行。
重点是看 回溯算法总结篇: