随想录训练营Day30 | 回溯 332.重新安排行程, 51. N皇后, 37. 解数独

65 阅读3分钟

随想录训练营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

  1. 理解为什么可以没有终止条件
  2. 理解递归的返回值为Boolean值
  3. 理解递归什么时候返回true----联系1)
  4. 理解递归什么时候返回false
  5. 思考为啥递归参数只有board
  6. 个人认为有待优化处: 能不能将这段代码修改一下,达到的目的是每进行下一次递归的时候, 找上一次递归成功填入数字的空格的右边一个空格, 而不是重新遍历整个棋盘,以提高搜索效率? ----也许可以尝试在递归参数加入之前填充的位置?
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