代码随想录刷题|LeetCode 332.重新安排行程 51. N皇后 37. 解数独

简介: 代码随想录刷题|LeetCode 332.重新安排行程 51. N皇后 37. 解数独

  这三道题目都是困难题目,都是根据代码随想录的思路总结书写,慢慢理解,慢慢熟练

332.重新安排行程

题目链接:力扣

思路


不会,只能看题解了


       这道题目主要要解决的几个问题:

               1、一个行程中,如果航班处理不好容易变成一个圈,成为死循环

               2、有多种解法,字母序靠前排在前面,让很多同学望而退步,如何该记录映射关系呢

               3、使用回溯法(也可以说深搜) 的话,那么终止条件是什么呢?

               4、搜索的过程中,如何遍历一个机场所对应的所有机场


       1、怎么保证航班不会死循环

               要保证航班不会死循环,首先要选择合适的存储航班信息的容器,才能更好地对航班信息进行处理


               需要对已经到达过的航班进行处理,要么删除存在容器中的航班信息,要么就对容器中的航班信息进行标记,这样才能避免航班进入死循环


       2、怎么记录航班的信息

               一个起始机场可能存在多个到达机场,所以需要使用map<起始机场,<到达机场,到达机场……>>这种数据结构进行存储


               因为要保证返回的排序是字典排序最小的形成组合,所以在存储到达机场的时候,应该按照顺序存储,java语言中能保证有序不可重复的集合有TreeSet和TreeMap


               使用TreeSet的话就要到达一个航班就删除一个航班,这样才能避免死循环

               使用TreeMap的话可以使用值来记录航班出现过的次数,来标记这个航班是否已经使用过了,相当于说我不删,而是做一个标记!


6281cfd953a248d8af208958613ee3cc.png


       3、终止条件怎么写

               回溯遍历的过程中,遇到的机场个数,如果达到了(航班数量+1),那么我们就找到了一个行程,把所有航班串在一起了


               本题的result就是记录路径的(就一条),在如下单层搜索的逻辑中result就添加元素了(因为已经排序过了,找到的第一条就是字典排序最前面的一条)


       4、如何遍历一个机场对应的所有机场

               在使用map集合映射机场的使用,遍历航班信息,将起始机场作为key,对应的所有到达机场作为value存放到一个集合中


               从"JFK"开始,先遍历"JFK"对应的所有到达机场,在遍历"JFK"对应的所有到达机场的所有到达机场,层层递归……当找到满足符合条件航线时,就返回true,此时航线变量res中存储的就是结果


11e466093bd54f9fb0bbd69f05fe2d6f.png


重新安排行程

       使用回溯的思路想法并不难理解,就是代码实现起来比较麻烦

               1、使用map集合实现对机票的映射信息

               2、递归map集合

               3、终止条件和收集结果的操作

class Solution {
    private LinkedList<String> res;
    // String 是起始机场
    // Map<String,Integer> 是到达机场和其航班次数
    private HashMap<String,TreeMap<String,Integer>> map;
    public List<String> findItinerary(List<List<String>> tickets) {
        // 初始化集合
        map = new HashMap<>();  // 使用HashMap实现大Map
        res = new LinkedList<>(); // 使用链表记录每条航线
        // 映射机票信息
        for (List<String> ticket : tickets) {
            TreeMap<String,Integer> temp;
            // 如果HashMap<String,TreeMap<String,Integer>> map 中已经有了这个起始航班,那就将其加入TreeMap<String,Integer>中
            // ticket中第一个元素是起始航班,第二元素是到达航班
            if (map.containsKey(ticket.get(0))) {
                temp = map.get(ticket.get(0));
                // ticket.get(1) 到达航班
                // temp.getOrDefault(t.get(1), 0) + 1 到达航班出现的次数
                temp.put(ticket.get(1),temp.getOrDefault(ticket.get(1), 0) + 1);
            } else {
                temp = new TreeMap<>(); // 有序(升序Map)
                temp.put(ticket.get(1), 1);
            }
            map.put(ticket.get(0),temp);
        }
        // 将起始机场添加进链表
        res.add("JFK");
        // 递归查找每一种路线
        backtracking(tickets.size());
        return res;
    }
    private boolean backtracking(int ticketNum) {
        if (res.size() == ticketNum + 1) {
            return true;
        }
        // 获取当前队列中的最后航班,按照这个航班寻找下一个可能的航班
        String last = res.getLast();
        if (map.containsKey(last)) {
            // 如果集合中存在最后航班的到达机场
            // map.get(last).entrySet() 获取起始航班对应的到达机场
            // Map.Entry<String,Integer> target 到达航班机场对应的set集合
            for( Map.Entry<String,Integer> target:map.get(last).entrySet() ) {
                // 获取该到达航班使用过的次数
                int count = target.getValue();
                // 如果等于0 说明这个航班已经使用过了,避免死循环
                // 如果大于0 说明这个航班还有次数
                if (count > 0) {
                    // 将合法航班添加到航线中
                    res.add(target.getKey());
                    target.setValue(count - 1);  // 使用过一次了
                    if (backtracking(ticketNum)) {
                        return true;
                    }
                    res.removeLast(); // 回溯
                    target.setValue(count);
                }
            }
        }
        return false;
    }
}

 

51. N皇后

题目链接:力扣

思路


N皇后是使用回溯算法的经典题目,棋盘的宽度就是for循环的长度,递归的深度就是棋盘的高度,这样就可以套进回溯法的模板里了


       棋盘上的皇后们的约束条件:

               1、不能同行 (递归的深度就自动检测了)

               2、不能同列

               3、不能同斜线


       要做好N皇后要做到三点:

               1、分清楚遍历的宽度和递归的深度

              ad30dafedd7d46a2b747b835ce685891.png

               2、怎么判断皇后位置是否合法

              b283dd0f18654702b6a5effd61e3a183.png

               3、怎么收集结果

             

75043d5b8a2f44ab857689f79a0994f0.png


       N皇后的思路并不难,难得是对各块的操作


       之前的回溯题目都是对一维数组进行处理的,N皇后是对二维数组进行处理


N皇后

class Solution {
    List<List<String>> result = new ArrayList<>();
    List<String> path = new ArrayList<>();
    // 主函数
    public List<List<String>> solveNQueens(int n) {
        char[][] chessboard = new char[n][n];
        for (char[] ch : chessboard) {
            Arrays.fill(ch,'.');
        }
        backtracking(n,0,chessboard);
        return result;
    }
    // 回溯函数
    // n 棋盘的列
    // row 棋盘的行
    // chessboard 皇后具体的位置
    void backtracking(int n,int row, char[][] chessboard) {
        // 当row走到最后一行的时候,就可以收集结果了
        if (row == n) {
            // 将二维数组中的内容转换成List<String>
            for (char[] ch : chessboard) {
                path.add(String.copyValueOf(ch));
            }
            result.add(new ArrayList(path));
            path.clear();
            return;
        }
        // 对棋盘进行递归
        for (int col = 0; col < n; col++) {
            // 判断皇后所在当前位置是否合法
            if (isVaild(row,col,n,chessboard)) {
                // 如果合法,就可以给当前棋盘位置赋值皇后
                chessboard[row][col] = 'Q';
                backtracking(n,row+1,chessboard);
                chessboard[row][col] = '.';
            }
        }
    }
    // 判断合法函数
    boolean isVaild(int row, int col, int n, char[][] chessboard){
        // 检查列
        for (int i = 0; i < row; i++) {
            if (chessboard[i][col] == 'Q') {
                return false;
            }
        }
        // 检查对角线
        // 检查45度对角线
        // 检查这一行上面的,从左边开始
        // i >=0 && j >=0 往45度 左上角 检测的界限
        for (int i = row - 1, j = col - 1; i >=0 && j >=0; i--,j--) {
            if (chessboard[i][j] == 'Q') {
                return false;
            }
        }
        // 检查135度对角线 
        // i >=0 && j <= n - 1 往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;
    }
}

37. 解数独

题目链接:力扣

思路

    解数独要比N皇后多一个维度,解数独和N皇后都是棋盘,行列都是相同的,不同的是N皇后初始化完二维数组后每一格的元素只有一种情况,但是解数独每一个有9中情况


       所以需要二维递归。一层递归是在当前格子,另一层递归是在后面的格子


       1、遍历棋盘格子

     

13720509a6a846ad993dfd3a4dfc5016.png


        2、递归当前格子的不同情况

     

8c96ec686a0c462f8ea3f172819eef62.png


       3、判断行合法

     

0ba06f24b959464f88bc0b1ed0fc081d.png


       4、判断列合法

     

b3c6b449b2e1491984af0dc02fc70177.png


       5、判断九宫格内合法

     

d19cda493e8f403c949f15bded633831.png


解数独

class Solution {
    public void solveSudoku(char[][] board) {
        backtracking(board);
    }
    // 递归函数
    private boolean backtracking(char[][] board){
        // 双层循环遍历棋盘上的网格
        // 一个for循环遍历棋盘的行,一个for循环遍历棋盘的列,一行一列确定下来之后,递归遍历这个位置放9个数字的可能性!
        for (int i = 0; i < 9; i++) { // 遍历行
            for (int j = 0; j < 9; j++) { // 遍历列
                if (board[i][j] != '.') { // 跳过原始数字
                    continue;  
                }
                // 每格上的元素从1-9 不断试验
                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] = '.';
                    }
                }
                // 9个数都不行就返回false
                return false;
                // 因为如果一行一列确定下来了,这里尝试了9个数都不行,说明这个棋盘找不到解决数独问题的解!
                // 那么会直接返回, 「这也就是为什么没有终止条件也不会永远填不满棋盘而无限递归下去!」
            }
        }
        // 遍历完没有返回false,说明找到了合适棋盘位置了
        return true;
    }
    // 判断是否重复合法
    /**
     * 判断棋盘是否合法有如下三个维度:
     *     同行是否重复
     *     同列是否重复
     *     9宫格里是否重复
     */
    boolean isValid(int row,int col, char val, char[][]board){
        // 同行是否重复
        for (int i = 0; i < 9; i++) {
            if (board[row][i] == val) {
                return false;
            }
        }
        // 同列是否重复
        for (int i = 0; i < 9; i++) {
            if (board[i][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;
    }
}
相关文章
|
3月前
|
Unix Shell Linux
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
本文提供了几个Linux shell脚本编程问题的解决方案,包括转置文件内容、统计词频、验证有效电话号码和提取文件的第十行,每个问题都给出了至少一种实现方法。
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
|
4月前
|
搜索推荐 索引 Python
【Leetcode刷题Python】牛客. 数组中未出现的最小正整数
本文介绍了牛客网题目"数组中未出现的最小正整数"的解法,提供了一种满足O(n)时间复杂度和O(1)空间复杂度要求的原地排序算法,并给出了Python实现代码。
124 2
|
1月前
|
机器学习/深度学习 人工智能 自然语言处理
280页PDF,全方位评估OpenAI o1,Leetcode刷题准确率竟这么高
【10月更文挑战第24天】近年来,OpenAI的o1模型在大型语言模型(LLMs)中脱颖而出,展现出卓越的推理能力和知识整合能力。基于Transformer架构,o1模型采用了链式思维和强化学习等先进技术,显著提升了其在编程竞赛、医学影像报告生成、数学问题解决、自然语言推理和芯片设计等领域的表现。本文将全面评估o1模型的性能及其对AI研究和应用的潜在影响。
40 1
|
3月前
|
数据采集 负载均衡 安全
LeetCode刷题 多线程编程九则 | 1188. 设计有限阻塞队列 1242. 多线程网页爬虫 1279. 红绿灯路口
本文提供了多个多线程编程问题的解决方案,包括设计有限阻塞队列、多线程网页爬虫、红绿灯路口等,每个问题都给出了至少一种实现方法,涵盖了互斥锁、条件变量、信号量等线程同步机制的使用。
LeetCode刷题 多线程编程九则 | 1188. 设计有限阻塞队列 1242. 多线程网页爬虫 1279. 红绿灯路口
|
2月前
|
存储 算法 C++
Leetcode第三十六题(有效的数独)
这篇文章介绍了如何使用C++编写一个算法来验证一个9x9数独是否有效,遵循数独的规则,即数字1-9在每一行、每一列和每个3x3宫内只能出现一次。
49 0
|
4月前
|
算法 Python
【Leetcode刷题Python】 LeetCode 2038. 如果相邻两个颜色均相同则删除当前颜色
本文介绍了LeetCode 2038题的解法,题目要求在一个由'A'和'B'组成的字符串中,按照特定规则轮流删除颜色片段,判断Alice是否能够获胜,并提供了Python的实现代码。
57 3
|
4月前
|
Python
【Leetcode刷题Python】50. Pow(x, n)
本文介绍了LeetCode第50题"Pow(x, n)"的解法,题目要求实现计算x的n次幂的函数,文章提供了递归分治法的详细解析和Python实现代码。
28 1
|
4月前
|
Python
【Leetcode刷题Python】LeetCode 478. 在圆内随机生成点
本文介绍了LeetCode 478题的解法,题目要求在给定圆的半径和圆心位置的情况下实现在圆内均匀随机生成点的功能,并提供了Python的实现代码。
34 1
|
4月前
|
算法 Python
【Leetcode刷题Python】73. 矩阵置零
本文介绍了LeetCode第73题的解法,题目要求在给定矩阵中将所有值为0的元素所在的行和列全部置为0,并提供了一种原地算法的Python实现。
36 0
【Leetcode刷题Python】73. 矩阵置零
|
4月前
|
存储 算法 索引
LeetCode第36题有效的数独
这篇文章介绍了LeetCode第36题"有效的数独"的解题方法,通过使用三个二维数组分别记录每一行、每一列以及每个3x3宫格内1-9数字出现的次数,来验证给定数独是否有效。