每日一练(day06-秒解皇后问题)

Posted 'or 1 or 不正经の泡泡

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了每日一练(day06-秒解皇后问题)相关的知识,希望对你有一定的参考价值。

文章目录

警句

自知之明是最难得的知识。——西班牙

题目

中序遍历

这里注意的就是输出,要给到一个列表里面,而不是直接打印即可。

输入:root = [1,null,2,3] 输出:[1,3,2] 示例 2:

输入:root = [] 输出:[] 示例 3:

输入:root = [1] 输出:[1]

class Solution 
    public List<Integer> inorderTraversal(TreeNode root) 
        List<Integer> res = new ArrayList<Integer>();
        inorder(root, res);
        return res;
    

    public void inorder(TreeNode root, List<Integer> res) 
        //中序遍历,将结果给到 res
        if (root == null) 
            return;
        
        inorder(root.left, res);
        res.add(root.val);
        inorder(root.right, res);
    

树的深度

给定一个二叉树,找出其最大深度。

二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。

说明: 叶子节点是指没有子节点的节点。

这个题目的代码非常简单,但是这里面涉及到递归的思想,而且有点抽象,我估计有很多人但是这个点理解地时候应该是不太顺利的,就像当初的KMP的next数组一样。

关于这个的写法,包括所有的递归都是有模板的。
思路:

1.输入输出
2.单层递归要做的事情,确定返回变量的格式输出
3.确定终止条件

代码:

输入输出
终止条件
每一步需要做的事情
返回参数格式、结算方式

技巧

明确单层递归要做的事情,只需要考虑到一开始和假设最后一次会发生什么即可,中间过程只会越想越乱

此外关于什么时候可以使用到递归,其实就是有个特点那就是整体可以拆分局部,可以使用栈的时候也可以使用(递归实际上使用的就是编译器提供的栈)

就拿这个为例子。一个数,输入根节点,如果有左右孩子,左右孩子又有节点,那么这个左右孩子也相当于一个根节点,那么从整体上,我就只需要考虑孩子节点,这样就是总体拆分成了局部,考虑自然就是局部。

回到本题代码,对于一个没有叶子节点的树而言,深度为以,左右孩子为空也就是深度为0。那么现在假设这个树,往上面加入一个节点此时,这个树的根节点就成为了另一棵树的叶子节点(假设为叶子节点),深度为1,执行代码的时候,此时返回结果为1给上一层假设上一层有右孩子,那么同样执行逻辑,假设返回给上一层的结果是2,那么对于这一层的深度不就是 max(left,right)同时加上自己,也就是 H=max(left,right)+1.那么就是三。下面就是假设的那课树

展开

class Solution 
    public int maxDepth(TreeNode root) 
        if(root == null)
            return 0;
        
        int left = this.maxDepth(root.left);

        int right = this.maxDepth(root.right);

        return Math.max(left,right)+1;

    

class Solution 
    public int maxDepth(TreeNode root) 
        if(root == null)
            return 0;
        
        
        return Math.max(this.maxDepth(root.left),this.maxDepth(root.right))+1;

    

较大分组位置

在一个由小写字母构成的字符串 s 中,包含由一些连续的相同字符所构成的分组。

例如,在字符串 s = “abbxxxxzyy” 中,就含有 “a”, “bb”, “xxxx”, “z” 和 “yy” 这样的一些分组。

分组可以用区间 [start, end] 表示,其中 start 和 end 分别表示该分组的起始和终止位置的下标。上例中的 “xxxx”
分组用区间表示为 [3,6] 。

我们称所有包含大于或等于三个连续字符的分组为 较大分组 。

找到每一个 较大分组 的区间,按起始位置下标递增顺序排序后,返回结果。

示例 1:

输入:s = “abbxxxxzzy” 输出:[[3,6]] 解释: “xxxx” 是一个起始于 3 且终止于 6 的较大分组 。
示例2:

输入:s = “abc” 输出:[] 解释:“a”,“b” 和 “c” 均不是符合要求的较大分组。
示例 3:

输入:s = “abcdddeeeeaabbbcd” 输出:[[3,5],[6,9],[12,14]] 解释:较大分组为 “ddd”,
“eeee” 和 “bbb” 示例 4:

输入:s = “aba” 输出:[]

这个没啥好说的。

class Solution 
    public List<List<Integer>> largeGroupPositions(String s) 
        List<List<Integer>> ret = new ArrayList<List<Integer>>();
        int n = s.length();
        int num = 1;
        for (int i = 0; i < n; i++) 
            if (i == n - 1 || s.charAt(i) != s.charAt(i + 1)) 
                if (num >= 3) 
                    ret.add(Arrays.asList(i - num + 1, i));
                
                num = 1;
             else 
                num++;
            
        
        return ret;
    


反转图像

给定一个二进制矩阵 A,我们想先水平翻转图像,然后反转图像并返回结果。

水平翻转图片就是将图片的每一行都进行翻转,即逆序。例如,水平翻转 [1, 1, 0] 的结果是 [0, 1, 1]。

反转图片的意思是图片中的 0 全部被 1 替换, 1 全部被 0 替换。例如,反转 [0, 1, 1] 的结果是 [1, 0, 0]。

示例 1:

输入:[[1,1,0],[1,0,1],[0,0,0]] 输出:[[1,0,0],[0,1,0],[1,1,1]] 解释:首先翻转每一行:
[[0,1,1],[1,0,1],[0,0,0]];
然后反转图片: [[1,0,0],[0,1,0],[1,1,1]] 示例 2:

输入:[[1,1,0,0],[1,0,0,1],[0,1,1,1],[1,0,1,0]]
输出:[[1,1,0,0],[0,1,1,0],[0,0,0,1],[1,0,1,0]] 解释:首先翻转每一行:
[[0,0,1,1],[1,0,0,1],[1,1,1,0],[0,1,0,1]];
然后反转图片: [[1,1,0,0],[0,1,1,0],[0,0,0,1],[1,0,1,0]]

这个没什么,就是双指针,然后套循环,之后的话,我们在反转,0–>1,1—>0 这里直接进行异或运算就行了。

class Solution 
    public int[][] flipAndInvertImage(int[][] image) 
        int n = image.length;
        for (int i = 0; i < n; i++) 
            int left = 0, right = n - 1;
            while (left < right) 
                if (image[i][left] == image[i][right]) 
                    image[i][left] ^= 1;
                    image[i][right] ^= 1;
                
                left++;
                right--;
            
            if (left == right) 
                image[i][left] ^= 1;
            
        
        return image;
    

N皇后问题

最后来一个皇后问题吧,这个应该是最经典的问题了。

题目

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

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

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

输入:n = 4
输出:[[".Q…","…Q",“Q…”,"…Q."],["…Q.",“Q…”,"…Q",".Q…"]]
解释:如上图所示,4 皇后问题存在两个不同的解法。 示例 2:

输入:n = 1 输出:[[“Q”]]

那么这个 皇后问题其实和我先前做数据结构实训课时候写的迷宫问题的思路其实是类似的,只是我们的规则不一样,先前的迷宫问题使用的DFS的思路,先直接往前走,返现路走不通了,返回,用一个栈来记录走过的路,然后出栈往回走,当然那里用递归也是可以的,不过你也需要用一个栈来存储你走过的路,只是不用循环罢了。
数据结构小设计–(迷宫问题Python版本)

规则

所以明确了我们使用DFS的思想,那么我们来说说这个皇后问题的规则,其实很简单,就是在一个棋盘里面,放置皇后,让皇后互相不能攻击有多少种放置方式。那么皇后攻击的范围如下图。

所以我们对于一个要放置的皇后,要保证这个放在的位置在水平,竖直方向,对角线方向上不能有别的皇后。否则就不行。

策略

现在我们已经知道了规则,那么接下来就是我们要大概怎么做。我们这边还是使用最经典的DFS来做一下。首先我们按照行优先嘛,在第一行第一列先试着放一下,然后放第二个,第二个肯定是在第二行,并且不可能在第一列。所以伪代码就出来了。


放置(0,棋盘)//从第0行开始放

放置(j,棋盘)
	for(int i=0;i<;i++)
		1.终止条件
		
		2.判断能不能放,能放就放并且往下再放
			放置(j+1,棋盘)//往下放第二个
			复原 //假设当前一轮放完了,我们就需要进行“洗牌”
	

这样一来我们就可以不断地去扫描了

判断条件

之后是我们的判断条件,整理有两个注意点,一个是判断当前点是否可以放置,这个我们已经知道了规则,那么就好办了。

放置判断

  public boolean isOk(char[][] chess,int row,int col)
        //列扫描,对角线扫描(上对角,下对角)
        for(int i=0;i<row;i++)
            if(chess[i][col]=='Q')
                return false;
            
        

        for(int i=row-1,j=col-1;i>=0&j>=0;i--,j--)
            if(chess[i][j]=='Q')
                return false;
            
        

        for(int i=row-1,j=col+1;i>=0&&j< chess.length;i--,j++)
            if(chess[i][j]=='Q')
                return false;
            
        

        return true;

    

我们是行放置的,所以只需要判断列和对角线即可。

找到解的判断

那么之后如何判断我们找到了解。
我们假设是 4 x 4的棋盘,如果第四个放得下去,那么按照逻辑,就会往下一行放,此时传递的行数就是4(从0开始)那么就说明前面的4x4都放好了,如果第四行就放不下去了,那么最多到3。之后我们将我们的结果放进去。

    public void solveN(char[][] chess,int row)
        if(row == chess.length)
            res.add(this.addsolved(chess));
        
        for(int j=0;j<chess[0].length;j++)
            if(isOk(chess,row,j))
                chess[row][j] = 'Q';
                solveN(chess,row+1);
                chess[row][j] = '.';//当前一轮结束了,复位
            
        
    

解题代码

那么之后就能解题目了

class Solution 
    List<List<String>> res = new ArrayList<>();
    public List<List<String>> solveNQueens(int n) 
        char[][] chess = new char[n][n];
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
                chess[i][j] = '.';
            
        

        solveN(chess,0);

        return res;
    
    public void solveN(char[][] chess,int row)
        if(row == chess.length)
            res.add(this.addsolved(chess));
        
        for(int j=0;j<chess[0].length;j++)
            if(isOk(chess,row,j))
                chess[row][j] = 'Q';
                solveN(chess,row+1);
                chess[row][j] = '.';//当前一轮结束了,复位
            
        
    

    public List<String> addsolved(char[][] chess)
        List<String> res1=new ArrayList<>();
        for(char[] c:chess)
            res1.add(new String(c));
        
        return res1;
    

    public boolean isOk(char[][] chess,int row,int col)
        //列扫描,对角线扫描(上对角,下对角)
        for(int i=0;i<row;i++)
            if(chess[i][col]=='Q')
                return false;
            
        

        for(int i=row-1,j=col-1;i>=0&j>=0;i--,j--)
            if(chess[i][j]=='Q')
                return false;
            
        

        for(int i=row-1,j=col+1;i>=0&&j< chess.length;i--,j++)
            if(chess[i][j]=='Q')
                return false;
            
        

        return true;

    

之后还有一个 N皇后问题2,就是叫你返回解的个数,一样的。

以上是关于每日一练(day06-秒解皇后问题)的主要内容,如果未能解决你的问题,请参考以下文章

蓝桥杯C/C++VIP试题每日一练之2n皇后问题

[ An Ac a Day ^_^ ] hdu 2553 N皇后问题 搜索

济南学习 Day 5 T3 am

每日一练(day12&PriorityQueue)

皇后游戏

生活日用算法——八皇后问题