随想录训练营Day30 | 回溯 332.重新安排行程, 51. N皇后, 37. 解数独
标签: LeetCode闯关记
332.重新安排行程(晕晕晕,有点乱,需要再捋捋)
问题
- Q:重复边问题是? 死循环问题是啥? 怎么解决的死循环问题?
- Q1:为啥还要用res?? res只需要保存一条成功路径,那么这样的话, 不如只用path记录当前遍历路径,如果当前遍历路径是找到的一个正确结果,直接输出不就可以了?
- Q2: 为啥字典序比较的是目的地的而不是出发地?
- Q3:有点晕了,为什么要用used而不用startIndex? 关键点
- key1: 字典序 :在这段给出的Java代码中,Collections.sort(tickets, (a, b) -> a.get(1).compareTo(b.get(1)))是对机票列表进行排序的操作。
- 该方法调用了Collections类中的sort方法,并传入两个参数:第一个参数是要排序的机票列表,第二个参数是一个Lambda表达式,用于指定比较两个机票的逻辑。
- 这个Lambda表达式(a, b) -> a.get(1).compareTo(b.get(1))将两个机票列表a和b作为输入参数,并使用String类的compareTo()方法比较它们的第二个元素(目的地机场代码)。 通过这种排序方式,可以确保在查找行程时按照目的地机场代码的字典顺序处理机票。
- key2: 返回值为Boolean, 只需要找到一条有效最小路径就可以,不用遍历
class Solution {
LinkedList<String> res;//Q1: 理解为啥还要用res?
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)));//Q2; key1
path.add("JFK");
boolean[] used = new boolean[tickets.size()]; //Q 2: used数组
Arrays.fill(used,false);
backTracing(tickets,used);
return res;
}
private boolean backTracing(List<List<String>> tickets, boolean[] used){//key2: 返回值为Boolean
if(path.size() == tickets.size() + 1){//终止条件
res = new LinkedList<String>(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(backTracing(tickets,used)){//精妙 一层层返回
return true;
}
used[i] = false;
path.removeLast();
}
}
return false;
}
}
time: 1h30min
51. N皇后
class Solution {
List<List<String>> res = new ArrayList<>();
char[][] chessboard;
public List<List<String>> solveNQueens(int n) {
chessboard = new char[n][n];
for (char[] c : chessboard) {
Arrays.fill(c,'.');
}
setQueen(chessboard,n,0);
return res;
}
public void setQueen(char[][] chessboard, int n, int row){
if(row == n){
res.add(ArrayList2(chessboard));
return;
}
for (int col = 0; col < n; col++) {
if(isValid(chessboard,n,row,col)){
chessboard[row][col] = 'Q';
setQueen(chessboard,n,row + 1);
chessboard[row][col] = '.';
}//错因:将"setQueen(chessboard,n,row + 1);
//chessboard[row][col] = '.';"放入if循环后了
}
}
public List ArrayList2(char[][]chessboard){//转成输入要求格式
List<String> path = new ArrayList<>();
for (char[] chars : chessboard) {
path.add(String.copyValueOf(chars));
}
return path;
}
public boolean isValid(char[][] chessboard, int n, int row, int col){
//检查列
for (int i = row - 1; i >= 0 ; 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 ; i--, j++) {
if(chessboard[i][j] == 'Q'){
return false;
}
}
return true;
}
}
time: 2h
37. 解数独
key point
- 理解为什么可以没有终止条件
- 理解递归的返回值为Boolean值
- 理解递归什么时候返回true----联系1)
- 理解递归什么时候返回false
- 思考为啥递归参数只有board
- 个人认为有待优化处: 能不能将这段代码修改一下,达到的目的是每进行下一次递归的时候, 找上一次递归成功填入数字的空格的右边一个空格, 而不是重新遍历整个棋盘,以提高搜索效率? ----也许可以尝试在递归参数加入之前填充的位置?
class Solution {
public void solveSudoku(char[][] board) {
fillSudoku(board);
}
public boolean fillSudoku(char[][] board){
for (int i = 0; i < board.length; i++) {
for (int j = 0; j < board[i].length; j++) {
if(board[i][j] != '.'){
continue;
}
for (char k = '1'; k <= '9' ; k++) {
if (isValid(board,i,j,k)){
board[i][j] = k;
if(fillSudoku(board)){
return true;
}
board[i][j] = '.';
}
}
return false;
}
}
return true;
}
public boolean isValid (char[][] board, int row, int col, char val){
//检查列
for (int i = 0; i < board.length; i++) {
if( board[i][col] == val){
return false;
}
}
//检查行
for (int j = 0; j < board[row].length; j++) {
if(board[row][j] == val){
return false;
}
}
//检查3*3
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;
}
}
time: 1h21min