day27 37. 解数独&&51. N 皇后&&332. 重新安排行程

  1. 解数独(37)
    问题描述
    给定一个9×9的数独棋盘,部分数字已填入,需要填充剩余的空格,使得每行、每列以及每个3×3的子网格内的数字均为1-9且不重复。
    代码实现
    你的代码使用了回溯法来解决数独问题。核心思路如下:
    预处理冲突状态:
    使用三个二维布尔数组 ca、cb 和 cc 分别记录行、列和九宫格的冲突状态。
    遍历棋盘,初始化这些冲突状态。
    回溯搜索:
    从左上角开始,逐个填充空格。
    对于每个空格,尝试填入1-9的数字,检查是否满足行、列和九宫格的约束。
    如果填入的数字合法,则递归处理下一个空格;如果递归返回 true,则成功;否则回溯并尝试下一个数字。
    如果所有数字都不满足,则返回 false。
    关键点
    冲突状态的高效存储:通过布尔数组快速判断数字是否可用。
    回溯法的终止条件:当遍历到棋盘外时,表示所有空格已正确填充。
    优化搜索顺序:跳过已填充的格子,直接找到下一个空格。
点击查看代码
//37. 解数独
    public void solveSudoku(char[][] board) {
        boolean[][] ca = new boolean[9][9];//记录行冲突状态
        boolean[][] cb = new boolean[9][9];//记录列冲突状态
        boolean[][] cc = new boolean[9][9];//记录九宫格冲突状态 index = (i/3) *3 +j/3
        for (int i = 0; i < 9; i++) {
            for (int j = 0; j < 9; j++) {
                if (board[i][j] != '.') {
                    char ch = board[i][j];
                    ca[i][ch-'1']=true;
                    cb[j][ch-'1']=true;
                    cc[(i/3) *3 +j/3][ch-'1']=true;
                }
            }
        }
        solve(0,0,board,ca,cb,cc);
    }
    private boolean solve(int i,int j,char[][] board,boolean[][] ca,boolean[][] cb,boolean[][] cc) {
        while (board[i][j] != '.') {
            j++;
            if (j >= 9) {
                j = 0;
                i++;
            }
            if (i>=9) return true;
        }
        for (int k = 1; k < 10; k++) {
            if (ca[i][k-1]||cb[j][k-1]||cc[(i/3) *3 +j/3][k-1]) continue;
            board[i][j]=(char)(k+'0');
            ca[i][k-1]=true;
            cb[j][k-1]=true;
            cc[(i/3) *3 +j/3][k-1]=true;
            if (solve(i,j,board,ca,cb,cc)) return true;
            board[i][j]='.';
            ca[i][k-1]=cb[j][k-1]=cc[(i/3) *3 +j/3][k-1]=false;
        }
        return false;
    }

  1. N皇后(51)
    问题描述
    在n×n的棋盘上放置n个皇后,使得它们互不攻击(即任意两个皇后不在同一行、同一列或同一斜线上)。返回所有可能的放置方案。
    代码实现
    你的代码同样使用了回溯法来解决N皇后问题。核心思路如下:
    冲突状态的存储:
    使用三个布尔数组 ca、cb 和 cc 分别记录列、右斜线和左斜线的冲突状态。
    列的索引为列号,右斜线的索引为 i + j,左斜线的索引为 n - 1 - (i - j)。
    回溯搜索:
    从第一行开始,逐行放置皇后。
    对于每一行的每一列,检查是否满足列和斜线的约束。
    如果合法,则递归处理下一行;如果递归返回成功,则将当前方案加入结果列表。
    回溯时恢复冲突状态。
    关键点
    冲突状态的高效存储:通过布尔数组快速判断皇后是否可以放置。
    回溯法的终止条件:当放置完所有行时,表示找到一个合法方案。
    优化存储:使用一维数组存储列和斜线的冲突状态,节省空间。
点击查看代码
//51. N 皇后
    public List<List<String>> solveNQueens(int n) {
        List<List<String>> res = new ArrayList<>();
        boolean[] ca = new boolean[n];//记录列冲突,行是层级,不用记录
        boolean[] cb = new boolean[2 * n - 1];//记录右斜线冲突 index=i+j
        boolean[] cc = new boolean[2 * n - 1];//记录左斜线冲突 index=(n-1)-(i-j)
        char[][] table = new char[n][n];
        for (int i = 0; i < n; i++) {
            Arrays.fill(table[i], '.');//Arrays.fill()只能填充一维数组
        }
        dfsSolveNQueens(0,n,table,ca,cb,cc,res);
        return res;
    }
    private void dfsSolveNQueens(int i,int n,char[][] table,boolean[] ca,boolean[] cb,boolean[] cc,List<List<String>> res){
        if (i==n){
            List<String> list = new ArrayList<>();
            for (char[] chars : table) {
                list.add(new String(chars));
            }
            res.add(list);
            return;
        }
        for (int j = 0; j < n; j++) {
            if (ca[j]||cb[i+j]||cc[n-1-(i-j)]) continue;
            table[i][j]='Q';
            cc[n-1-(i-j)] =cb[i+j] =ca[j] = true;
            /*cb[i+j] = true;
            cc[n-1-(i-j)] = true;*/
            dfsSolveNQueens(i+1,n,table,ca,cb,cc,res);
            table[i][j]='.';
            cc[n-1-(i-j)] =cb[i+j] =ca[j] = false;
            /*cb[i+j] = false;
            cc[n-1-(i-j)] = false;*/
        }
    }
  1. 重新安排行程(332)
    问题描述
    给定一系列机票,每张机票包含起点和终点。要求重新安排行程,使得行程按字典序最小。
    代码实现
    你的代码使用了深度优先搜索(DFS)+ 邻接表来解决行程问题。核心思路如下:
    构建图:
    使用哈希表 graph 存储邻接表,键为起点,值为优先队列(存储按字典序排列的终点)。
    DFS搜索:
    从起点 "JFK" 开始,按字典序选择下一个目的地。
    每次选择一个目的地后,递归处理下一个节点。
    回溯时将当前机场加入路径(使用链表的 addFirst 方法,确保路径按逆序存储)。
    优化:
    使用优先队列确保每次选择的下一个目的地是字典序最小的。
    使用链表存储路径,方便在回溯时插入当前节点。
    关键点
    图的存储:使用邻接表存储图,方便按字典序选择下一个节点。
    DFS的终止条件:当所有机票都使用完时,返回路径。
    回溯法的优化:通过链表的逆序存储,避免额外的路径反转操作。
点击查看代码
//332. 重新安排行程
    /*List<String> res ;//效率太低
    public List<String> findItinerary(List<List<String>> tickets) {
        LinkedList<String> path = new LinkedList<>();
        boolean[] used = new boolean[tickets.size()];
        path.add("JFK");
        dfsFindItinerary(tickets,used,path,"JFK");
        return res;
    }
    private void dfsFindItinerary(List<List<String>> tickets,boolean[] used,LinkedList<String> path,String start) {
        if (path.size() == tickets.size()+1) {
            if (res==null) {
                res = new ArrayList<>(path);
            } else {
                for (int i = 0; i < res.size(); i++) {
                    if(res.get(i).compareTo(path.get(i))>0){
                        res=new ArrayList<>(path);
                        break;
                    } else if (res.get(i).compareTo(path.get(i))<0) {
                        break;
                    }
                }
            }
            return;
        }
        for (int i = 0; i < tickets.size(); i++) {
            if (used[i]||(!tickets.get(i).get(0).equals(start))) continue;
            path.add(tickets.get(i).get(1));
            used[i] = true;
            dfsFindItinerary(tickets,used,path,tickets.get(i).get(1));
            path.removeLast();
            used[i] = false;
        }
    }*/
    // 使用邻接表存储图
    private Map<String, PriorityQueue<String>> graph;

    public List<String> findItinerary(List<List<String>> tickets) {
        // 初始化邻接表
        graph = new HashMap<>();
        for (List<String> ticket : tickets) {
            String from = ticket.get(0);
            String to = ticket.get(1);
            if(!graph.containsKey(from)) graph.put(from, new PriorityQueue<>());// 使用优先队列保持字典序
            graph.get(from).offer(to);
        }

        LinkedList<String> path = new LinkedList<>();
        dfs("JFK", path);
        return path;
    }

    private void dfs(String airport, LinkedList<String> path) {
        PriorityQueue<String> destinations = graph.get(airport);
        while (destinations != null && !destinations.isEmpty()) {
            String next = destinations.poll(); // 按字典序选择下一个目的地
            dfs(next, path);
        }
        // 将当前机场加入路径(回溯时添加到路径中),终点最先添加
        path.addFirst(airport);
    }
posted @ 2025-02-18 21:45  123木头人-10086  阅读(38)  评论(0)    收藏  举报