二刷LeetCode:“51.N皇后 37.解数独”题解心得(简单易懂)

news2024/11/17 14:28:10

引言(初遇噩梦,再遇坦然)

在阅读本文之前,建议大家已经接触过回溯算法,并完成回溯相关题目,例如:子集问题、组合问题、排列问题
子集:子集II、子集
组合:组合、组合总和、组合总和II
排列:全排列、全排列II

🍏我第一次尝试这两道回溯算法题是在2023年的冬天。那一年,西安的冬天冷得让人直哆嗦,而在暖和得几乎让人犯困的图书馆里,这两道题却让我的心情比外面的天气还要凉快几分,简直是雪上加霜,冷到心坎里去了。之前跟着 《代码随想录》 刷题时,前面几道关于子集、组合和排列的问题简直就像是小菜一碟,让我一度觉得自己又行了。结果呢?这两道‘困难’级别的题目直接给我来了个下马威。如果你第一次就能把解析从头到尾捋个差不多,那你的水平就很NB了!(反正我当时是直接懵圈,只能尴尬而不失礼貌地保持沉默)

时隔将近一年的时间,虽然之后的这个夏天没怎么刷题吧(一段不算很差的实习经历~实在没时间精力去刷题了~~~):

在这里插入图片描述

🌟一年的时光悄然流逝,虽然这一年我没有疯狂刷题,但当我再次面对这两道曾经让我头疼的题目时,内心竟然出奇地平静。曾经的我,一心只想着如何破题,怎么解题如今的我,却更加关注解决问题的方法和背后的思路,这种感觉就像拨云见日般清晰。虽然这次我还是没能完全独立写出解答,但至少我已经不再像从前那样一头雾水,而是学会了逐步推导和思考。

🍎我想说的是,刷题其实是一个渐进的过程,第一次遇到难题看不懂是很正常的,不必死磕。有时候,‘简单题’未必真简单,而‘困难题’也未必无从下手。记住,积少成多,聚沙成塔。人总是在不断成长的,只要我们坚持不懈地提升自己,充实自己,曾经的难题终将成为过去式。

🍊共勉之,相信不久的将来,曾经困扰我们的题目也会迎刃而解。

好了,不说废话了,步入正轨吧😂

在这里插入图片描述
在这里插入图片描述

力扣第51题N皇后第37题解数独其实大致思路是差不多的,都是基于我们在很多平台看到的回溯算法框架,例如:

void backtrack(路径,选择列表) {
    if (满足结束条件) {
        res.add(路径);
        return;
    }
    for (选择:选择列表) {
        做选择;
        backtrack(路径,选择列表); // 递归
        撤销选择;
    }
}

其实回溯算法就是我们常说的DFS算法,本质上就是一种暴力穷举算法,解决一个回溯问题,实际上就是一个决策树的遍历过程。其核心就是for循环里面的递归,在递归调用之前“做选择”,在递归调用之后“撤销选择”,特别简单!

在这里插入图片描述
for 循环可以看作是水平方向上的遍历,而递归则是垂直方向上的遍历,这样结合起来就能遍历整个树状结构。通常,当搜索达到最深的层次(叶子节点)时,就意味着找到了一个完整的解。(这个很重要,后续便于理解)

经典回顾

51. N皇后

按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。

n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。

给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。

每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中'Q' 和 '.'分别代表了皇后和空位。

在这里插入图片描述

思路解读

在写这道题目之前,首先要明确它和我们之前做的其他回溯算法的题目有什么区别。都知道N皇后问题是回溯算法解决的经典问题,但是用回溯解决多了组合、切割、子集、排列问题之后,遇到这种二维矩阵还会有点不知所措,还多了一些规则约束:

  1. 同一行不能有皇后
  2. 同一列不能有皇后
  3. 同一斜线不能有皇后

这个问题的本质其实和全排列问题差不多,决策树的每一层表示棋盘上的每一行;每个节点可以做出的选择是,在该行的任意一列放置一个皇后。这里借随想录的树形结构举例:
在这里插入图片描述

实际上,我们的目标是到达叶子节点,这样就可以确定皇后的确切位置。总的来说,N 皇后问题相比普通的全排列问题,只是多了一些规则约束。而在 Java 中,仅仅是多了将数组转换为集合的操作。逐步拆解后,你会发现其实并没有那么复杂!下面开始细细分析一波:

本题我用的是Java语言编写分析,C++代码见后文即可!Java操作字符串这些的其实真的麻烦!

class Solution {
	// 力扣的方法签名 
    public List<List<String>> solveNQueens(int n) {
    }
}

正常情况下我们拿到一个回溯算法,大多数都要定义两个全局变量(res和track),比如这样:

class Solution {
	List<List<String>> res = new ArrayList<>();
	List<String> track = new ArrayList<>();
	// 力扣的方法签名 
    public List<List<String>> solveNQueens(int n) {
    }
}

但是仔细看看这个题你会发现它其实是一个List集合里面包含了二维数组,类似于就是result集合里面包含的track子集合一样。所以我们不得不再写一个方法,将我们操作的二维数组转换为track集合。所以就有了如下代码:

class Solution {
	List<List<String>> res = new ArrayList<>();
	// 力扣的方法签名 
    public List<List<String>> solveNQueens(int n) {
    	
    }
    
    // 二维数组转List
    public List Array2List(char[][] chessboard) {
    	// 这个就是我们最终返回的“track”集合!
        List<String> track = new ArrayList<>();

        for (char[] c : chessboard) {
            track.add(String.copyValueOf(c));
        }
        return track;
    }
}

对二维数组进行初始化:

class Solution {
    List<List<String>> res = new ArrayList<>();
    public List<List<String>> solveNQueens(int n) {
    	// 注意这里是字符数组
        char[][] chessboard = new char[n][n];
        // 让每一行先填满“.”  后面在指定位置放置皇后“Q”
        for (char[] c : chessboard) {
            Arrays.fill(c, '.');
        }
        // 参数待定
        backTrack(...);
        return res;
    }	
}

按照前文提供的回溯模版,我们来分析一下:

void backtrack(路径,选择列表) {
    if (满足结束条件) {
        res.add(路径);
        return;
    }
    for (选择:选择列表) {
        做选择;
        backtrack(路径,选择列表); // 递归
        撤销选择;
    }
}

参数n是棋盘的大小,用row来记录当前遍历到棋盘的第几层了。

	void backTrack(int n,int row,char[][] chessboard){
		if(...){
		}
		for(...){
		}
	}

前面说过,当我们遍历到叶子结点的时候,就可以收获结果了。

	void backTrack(int n,int row,char[][] chessboard){
		if(row == chessboard.length){
			// 这里相当于就是
			// res.add(new ArrayList<>(track));
            res.add(Array2List(chessboard));
            return;
        }
		for(...) {
		}
    }

接下来看看单层for循环要做的事情,无非就是判断当前位置能否放置皇后’Q’的问题,而且每次都是要从新的一行的起始位置开始搜,所以都是从0开始。

	void backTrack(int n,int row,char[][] chessboard){
		if(...){
			...
        }
		for(int i = 0;i < n;i++) {
			// 通过isValid函数进行剪枝
			if(isValid(...)){
				chessboard[row][i] = 'Q';
				// 这里每递归一次,深度都要+1
				backtrack(n,row+1,chessboard);
				chessboard[row][i] = '.';
			}
		}
    }

OK,写到这基本上就已经结束了,剩下的就是验证棋盘是否合法就行了,这一块就纯画图写代码,没有什么难的了。所以我们的isValid函数需要三个参数,一个就是chessboard数组,一个就是row行数,一个就是col列数。通过这三个参数去判断是否合理。

在这里插入图片描述

    boolean isValid(char[][] chessboard,int row,int col){
        int n = chessboard.length;
        // 检查列
        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<n;i--,j++){
            if(chessboard[i][j] == 'Q'){
                return false;
            }
        }

        // 检查左上斜线(135度)
        for(int i = row-1, j = col-1;i>=0&&j>=0;i--,j--){
            if(chessboard[i][j] == 'Q'){
                return false;
            }
        }

        return true;
    }

这里大家肯定会问,按照N皇后的规则,为什么不检查左下角、右下角和下方的格子啊?为啥只检查了左上角、右上角和上方的格子呢?原因也很简单,其实我们放置皇后的时候是一层一层,由上到下放置的,所以下方的格子根本不需要检查(还没放皇后呢),又因为一行只能放置一个皇后,所以每行也不用检查了。最后只检查正上方(列)、左上、右上三个方向即可。

代码实现

Java

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, '.');
        }
        // 注意这里从数组第0行开始的
        backTrack(n, 0, chessboard);
        return res;
    }

    void backTrack(int n,int row,char[][] chessboard){
        if(row == chessboard.length){
            res.add(Array2List(chessboard));
            return;
        }

        for(int i = 0;i < n;i++){
            if(isValid(chessboard,row,i)){
                chessboard[row][i] = 'Q';
                backTrack(n,row+1,chessboard);
                chessboard[row][i] = '.';
            }
            
        }
    }

    // 二维数组转List
    public List Array2List(char[][] chessboard) {
        List<String> list = new ArrayList<>();

        for (char[] c : chessboard) {
            list.add(String.copyValueOf(c));
        }
        return list;
    }

    boolean isValid(char[][] chessboard,int row,int col){
        int n = chessboard.length;
        // 检查列
        for(int i = 0;i<row;i++){
            if(chessboard[i][col] == 'Q'){
                return false;
            }
        }

        // 检查右上
        for(int i = row-1, j = col+1;i>=0&&j<n;i--,j++){
            if(chessboard[i][j] == 'Q'){
                return false;
            }
        }

        // 检查左上
        for(int i = row-1, j = col-1;i>=0&&j>=0;i--,j--){
            if(chessboard[i][j] == 'Q'){
                return false;
            }
        }

        return true;
    }
}

C++

class Solution {
private:
vector<vector<string>> result;
// n 为输入的棋盘大小
// row 是当前递归到棋盘的第几行了
void backtracking(int n, int row, vector<string>& chessboard) {
    if (row == n) {
        result.push_back(chessboard);
        return;
    }
    for (int col = 0; col < n; col++) {
        if (isValid(row, col, chessboard, n)) { // 验证合法就可以放
            chessboard[row][col] = 'Q'; // 放置皇后
            backtracking(n, row + 1, chessboard);
            chessboard[row][col] = '.'; // 回溯,撤销皇后
        }
    }
}
bool isValid(int row, int col, vector<string>& chessboard, int n) {
    // 检查列
    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; i--, j++) {
        if (chessboard[i][j] == 'Q') {
            return false;
        }
    }
    return true;
}
public:
    vector<vector<string>> solveNQueens(int n) {
        result.clear();
        std::vector<std::string> chessboard(n, std::string(n, '.'));
        backtracking(n, 0, chessboard);
        return result;
    }
};

相关题目: 52. N皇后II

这道题和「51. N 皇后」非常相似,区别在于,第 51 题需要得到所有可能的解,这道题只需要得到可能的解的数量。因此这道题可以使用第 51 题的做法,只需要将得到所有可能的解改成得到可能的解的数量即可。

在这里插入图片描述


37. 解数独

编写一个程序,通过填充空格来解决数独问题。

数独的解法需 遵循如下规则:

数字 1-9 在每一行只能出现一次
数字 1-9 在每一列只能出现一次
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图)
数独部分空格内已填入了数字,空白格用'.'表示。
在这里插入图片描述
在这里插入图片描述

思路解读

这个题就和上面的N皇后问题有些不一样了,N皇后问题是每一行每一列只放一个皇后,只需要一层for循环遍历一行,递归来遍历列,然后一行一列确定皇后的唯一位置。

本题就不一样了,本题中棋盘的每一个位置都要放一个数字(而N皇后是一行只放一个皇后),并检查数字是否合法,解数独的树形结构要比N皇后更宽更深。这里借随想录的树形结构举例:

在这里插入图片描述

比较巧妙的是,递归函数的返回值需要是boolean类型,为什么呢?

因为解数独找到一个符合的条件(就在树的叶子节点上)立刻就返回,相当于找从根节点到叶子节点一条唯一路径,所以需要使用boolean返回值。

class Solution {
    public void solveSudoku(char[][] board) {
        backtrack(board);
    }

	boolean backtrack(char[][] board){
		...
	}
}

本题递归不用终止条件,解数独是要遍历整个树形结构寻找可能的叶子节点就立刻返回。递归的下一层的棋盘一定比上一层的棋盘多一个数,等数填满了棋盘自然就终止(填满当然好了,说明找到结果了),所以不需要终止条件!如果一行一列确定下来了,尝试了9个数都不行,说明这个棋盘找不到解决数独问题的解!

那么会直接返回, 这也就是为什么没有终止条件也不会永远填不满棋盘而无限递归下去

核心代码:

    boolean backtrack(char[][] board){
    	// 遍历行
        for(int i =0;i<board.length;i++){
        	// 遍历列
            for(int j =0;j<board[0].length;j++){
            	// 如果该位置有数字则跳出当前循环
                if(board[i][j] != '.') continue;
                // 找到一个具体位置了,开始放1-9试试
                for(char k = '1';k<='9';k++){
                	// 判断是否满足数独条件
                    if(isValid(...)){
                        board[i][j] = k;
                        // 找到合适一组立刻返回
                        // 这里相当于遍历完整个棋盘后,把结果一层一层返回上来
                        if(backtrack(board)) return true;
                        board[i][j] = '.';
                    }
                } 
                return false;   // 9个数都试完了,都不行,那么就返回false
            }
        }
        return true;  // 遍历完没有返回false,说明找到了合适棋盘位置了
    }

然后就是判断棋盘是否合法:

  • 同行是否重复
  • 同列是否重复
  • 九宫格里是否重复

这个就是一个模拟的过程了,一步一步写出循环就行,最难理解的回溯我们已经写完了。老样子,我们必须得传一个board数组,row和col来确定其位置,还有就是当前位置放的数字合不合理。

boolean isValid(char[][] board,int row,int col,char key){
        // 同一行中是否有重复的数字
        for(int i = 0;i < 9;i++){
        	if(board[row][i] == key){
        		return false;
        	}
        }

        // 同一列中是否有重复的数字
        for(int i = 0;i < 9;i++){
        	if(board[i][col] == key){
        		return false;
        	}
        }

        // 9宫格中是否有重复的数字
        // 这里的startRow和startCol都是为了确保能从正确的起始位置开始
        // 你可以想一想如果我在第二行第二列呢?
        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] == key){
                    return false;
                }
            }
        }
        return true;
}

代码实现

Java

class Solution {
    public void solveSudoku(char[][] board) {
        backtrack(board);
    }

    boolean backtrack(char[][] board){
        for(int i =0;i<board.length;i++){
            for(int j =0;j<board[0].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(backtrack(board)) return true;
                        board[i][j] = '.';
                    }
                } 
                return false;
            }
        }
        return true;
    }

    boolean isValid(char[][] board,int row,int col,char key){
        // 同一列中是否有重复的数字
        for(int i=0;i<9;i++){
            if(board[i][col] == key){
                return false;
            }
        }

        // 同一行中是否有重复的数字
        for(int j = 0;j<9;j++){
            if(board[row][j] == key){
                return false;
            }
        }

        // 九宫格中是否有重复的数字
        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] == key){
                    return false;
                }
            }
        }
        return true;
    }
}

C++

class Solution {
private:
bool backtracking(vector<vector<char>>& board) {
    for (int i = 0; i < board.size(); i++) {        // 遍历行
        for (int j = 0; j < board[0].size(); j++) { // 遍历列
            if (board[i][j] == '.') {
                for (char k = '1'; k <= '9'; k++) {     // (i, j) 这个位置放k是否合适
                    if (isValid(i, j, k, board)) {
                        board[i][j] = k;                // 放置k
                        if (backtracking(board)) return true; // 如果找到合适一组立刻返回
                        board[i][j] = '.';              // 回溯,撤销k
                    }
                }
                return false;  // 9个数都试完了,都不行,那么就返回false
            }
        }
    }
    return true; // 遍历完没有返回false,说明找到了合适棋盘位置了
}
bool isValid(int row, int col, char val, vector<vector<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;
        }
    }
    int startRow = (row / 3) * 3;
    int startCol = (col / 3) * 3;
    for (int i = startRow; i < startRow + 3; i++) { // 判断9方格里是否重复
        for (int j = startCol; j < startCol + 3; j++) {
            if (board[i][j] == val ) {
                return false;
            }
        }
    }
    return true;
}
public:
    void solveSudoku(vector<vector<char>>& board) {
        backtracking(board);
    }
};

相关题目:36.有效的数独

这道题相比「37. 解数独」就简单许多了这道题只要判断已存在于棋盘上的数字是否满足规则约束就行,不需要我们自己填满棋盘再去判断了。当然,在判断时需要对isValid()方法做一点改动。这里直接给出代码:

在这里插入图片描述

class Solution {
    public boolean isValidSudoku(char[][] board) {
        for (int i = 0; i < board.length; i++) {
            for (int j = 0; j < board[0].length; j++) {
                char key = board[i][j];
                if (key != '.' && !isValid(board, i, j, key)) {
                    return false;
                }
            }
        }
        return true;
    }

    // 注意这里,一定要跳过当前的数字啊,要不然会错误地返回false
    boolean isValid(char[][] board, int row, int col, char key) {
        // 同一列中是否有重复的数字
        for (int i = 0; i < 9; i++) {
            if (i != row && board[i][col] == key) {
                return false;
            }
        }

        // 同一行中是否有重复的数字
        for (int j = 0; j < 9; j++) {
            if (j != col && board[row][j] == key) {
                return false;
            }
        }

        // 3x3 宫格中是否有重复的数字
        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 ((i != row || j != col) && board[i][j] == key) {
                    return false;
                }
            }
        }
        
        return true;
    }

}

⚠️这里需要注意了:

这道题(36.有效的数独) 如果沿用上面解数独的isValid逻辑大体上是正确的,但它有一个关键问题:没有跳过当前要检查的数字

具体来说,在 isValid 方法中,遍历同一行、同一列以及 3x3 宫格时,会再次检查 board[row][col],即当前正在检查的数字自身,这就导致错误地返回 false。为了解决这个问题,需要在检查时跳过当前的位置。

而这个方法在37. 解数独时有效的原因在于你传入的数字是准备放入的数字,而不是当前已经存在的数字。因此,在检查过程中,不会遇到检查自身数字的问题。

在这里插入图片描述

心得体会

✍️看到这里,相信现在你对这两道经典的回溯算法题有更深层次的认知了,如果还是觉得难没关系,一步一脚印,慢慢来,觉得难就不做了呗!咱好好地睡一觉,哪天心情好了再看看这破题~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2162665.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[spring]用MyBatis XML操作数据库 其他查询操作 数据库连接池 mysql企业开发规范

文章目录 一. MyBatis XML配置文件1. 配置链接字符串和MyBatis2. 写持久层代码方法定义Interface方法实现xml测试 3. 增删改查增:删改查 二. 开发规范(mysql)三. 其他查询操作1. 多表查询2. #{} 和 ${}(面试题)使用区别 排序功能like查询 三. 数据库连接池 一. MyBatis XML配置…

【华为】用策略路由解决双出口运营商问题

需求描述 不同网段访问互联网资源时&#xff0c;走不同的出口&#xff0c;即PC1走电信出口&#xff0c;PC2走移动出口。 客户在内网接口下应用策略路由后往往出现无法访问内网管理地址的现象&#xff0c;该举例给出解决办法。 拓扑图 基础配置 #sysname R1 # # interface G…

【hot100-java】【下一个排列】

R8-技巧篇 最近速成java中&#xff0c;算法基础需要兼顾。 class Solution {public void nextPermutation(int[] nums) {int lennums.length;List<Integer>list new ArrayList<>();boolean flagtrue;for (int ilen-1;i>0;i--){list.add(nums[i]);Collections.…

若依生成主子表

一、准备工作 确保你已经部署了若依框架&#xff0c;并且熟悉基本的开发环境配置。同时&#xff0c;理解数据库表结构对于生成代码至关重要。 主子表代码结构如下&#xff08;字表中要有一个对应主表ID的字段作为外键&#xff0c;如下图的customer_id&#xff09; -- ------…

Pyinstaller打包python程序为exe时 程序多线程导致打开非常多窗口解决

装了个Pyinstaller打包exe pip install Pyinstaller 打包命令 Pyinstaller -F main.py Pyinstaller -F -w main.py #不带控制台 Pyinstaller -F -w -i 1.ico main.py #指定图标不带控制台 打包完的exe一运行开了一坨窗口&#xff0c;一眼多线程&#xff0c;我程序里的多线程如…

RtspServer:轻量级RTSP服务器和推流器

文章目录 项目概述技术分析支持的编码格式传输方式心跳检测机制RTSP 推流安全性 架构分析RtspServer 整体架构流程分析1. 客户端连接和会话建立2. 媒体数据传输3. 心跳检测和连接维护 xop 基础库项目介绍功能特性xop 整体架构 应用场景社区问题收集与解答问题一&#xff1a;刚开…

C++:deque的底层原理

一、deque是是双端队列。 deque(双端队列)&#xff1a;是一种双开口的"连续"空间的数据结构&#xff0c;双开口的含义是&#xff1a;可以在头尾两端进行插入和删除操作&#xff0c;且时间复杂度为O(1)&#xff0c;与vector比较&#xff0c;头插效率高&#xff0c;不…

Docker自定义构建镜像dockerfile和使用数据卷

Docker自定义构建镜像dockerfile和使用数据卷 DockerFile Dockerfile 是一个文本文件&#xff0c;包含了一系列用于构建 Docker 镜像 的指令和配置信息。通过编写 Dockerfile &#xff0c;用户可以定义镜像的构建过程&#xff0c;包括基础镜像、运行命令、设置环境变量、暴露…

38.重复的子字符串

方法1&#xff1a; class Solution {public boolean repeatedSubstringPattern(String s) {if (s.equals("")) return false;String s2(ss).substring(1,(ss).length()-1);//去掉首尾字符return s2.contains(s);//判断是否包含s} } class Solution(object):def rep…

Leetcode 相交链表

一图胜千言&#xff0c;java 代码如下&#xff1a; /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode(int x) {* val x;* next null;* }* }*/ public class Solution {public ListN…

Python自动化测试之unittest框架

一、什么是框架 框架是由大佬开发或者专业的研发团队研发的技术骨架&#xff0c;框架是一个半成品&#xff0c;框架是对常用的功能&#xff0c;基础的代码进行封装的一个工具&#xff0c;这个工具对外提供了一些API&#xff0c;其他的开发者只需要调用框架的接口即可&#xff…

网络安全法中,个人信息保护的措施和原则有哪些?

《中华人民共和国网络安全法》中关于个人信息保护的规定强调了几项基本原则和措施&#xff0c;以确保个人信息的安全。以下是其中的一些要点&#xff1a; 原则 合法性&#xff1a;个人信息的收集和使用必须符合法律规定。 正当性&#xff1a;信息收集和使用的目的是正当的&…

数据的基本统计描述

基本操作 首先导入鸢尾花数据集用于分析 import numpy as np from sklearn.datasets import load_iris iris_dataload_iris() iris_data 查看数据维度 iris_data.data.shape 获取第一组数据 iris_data.data[0] 获取第一个维度所有取值&#xff0c;作为一维向量 iris_…

【高分系列卫星简介——高分五号卫星(GF-5)】

高分五号卫星&#xff08;GF-5&#xff09; 高分五号&#xff08;GF-5&#xff09;卫星是中国高分辨率对地观测系统重大专项系列中的一颗重要卫星&#xff0c;主要承担着遥感、测绘等任务。以下是对高分五号卫星的详细介绍&#xff1a; 一、基本信息 国籍&#xff1a;中国研…

内容生态短缺,Rokid AR眼镜面临市场淘汰赛

AR是未来&#xff0c;但在技术路径难突破、生态系统难建设&#xff0c;且巨头纷纷下场的背景下&#xff0c;Rokid能坚持到黎明吗&#xff1f; 转载&#xff1a;科技新知 原创 作者丨王思原 编辑丨蕨影 苹果Vision Pro的成功量产和发售&#xff0c;以及热门游戏《黑神话》等在A…

解锁微软录屏工具:2024 开启屏幕录制新时代

现在快节奏的生活环境&#xff0c;录屏工具已成为不可或缺的记录利器&#xff0c;其应用范围广泛。若你正对windows自带录屏功能充满好奇&#xff0c;渴望掌握其操作方法&#xff0c;或是寻求更多专业、便捷的录屏软件选项&#xff0c;那么就请继续阅读吧。 1.福昕录屏大师 链…

清华大学开源视频转文本模型——CogVLM2-Llama3-Caption

通常情况下&#xff0c;大多数视频数据并不附带相应的描述性文本&#xff0c;因此有必要将视频数据转换为文本描述&#xff0c;为文本到视频模型提供必要的训练数据。 CogVLM2-Caption 是一个视频字幕模型&#xff0c;用于为 CogVideoX 模型生成训练数据。 文件 使用 import i…

应用层 II(文件传输协议FTP)【★★】

&#xff08;★★&#xff09;代表非常重要的知识点&#xff0c;&#xff08;★&#xff09;代表重要的知识点。 一、文件传输协议&#xff08;FTP&#xff09; 文件传送协议 FTP&#xff08;File Transfer Protocol&#xff09;是互联网上使用得最广泛的文件传送协议。FTP 提…

14年408-计算机网络

第一题&#xff1a; 解析&#xff1a;OSI体系结构 OSI由下至上依次是&#xff1a;物理层-网络链路层-网络层-运输层-会话层-表示层-应用层。 因此直接为会话层提供服务的是运输层。答案选C 第二题&#xff1a; 解析&#xff1a;数据链路层-交换机的自学习和帧转发 主机a1向交换…

零基础学Axios

Axios官网&#xff1a;Axios官网 想用Axios前需要在项目中安装axios,安装方式如下&#xff1a; 下列是axios请去方式&#xff0c;本文主要讲解post和get请求&#xff0c;其他请求和这两种请求方法相同。 1 get请求 1.1 不带请求参数 前端 后端 1.2 带请求参数 前端 写法…