• Leetcode 易错题整理(三)73. 77. 78. 81. 90. 95.105. 130.


    73. 矩阵置零

    给定一个 *m* x *n* 的矩阵,如果一个元素为 0 ,则将其所在行和列的所有元素都设为 0 。请使用 原地 算法**。**

    示例 1:

    img

    输入:matrix = [[1,1,1],[1,0,1],[1,1,1]]
    输出:[[1,0,1],[0,0,0],[1,0,1]]
    
    • 1
    • 2

    笨方法:先遍历一次整个数组,获取到所有0位置,第二次把这里面所有0位置数字横竖都变为0.

    原地算法:我们可以把这一行列是否有0的信息存到第一行和第一列,比如该数组第一次扫描完结果为:

    1 0 1

    0 0 1

    1 1 1

    上和左两个0表示这一行这一列后面应当被置零。第二次扫描的时候就只扫描第一行和第一列,把为0的行列头这一行列都置为0即可。

    但是这样会出现一个问题,就是比如第一行第一列如果出现了0,那么第一行 第一列也要被置为0. 但是我们无法判断这个0是本来就有的还是第一次遍历的时候数组中的元素传递过来的。

    解决办法是我们再单独用两个变量存储第一行第一列是否存在0的信息,如果存在,那么最后再把第一行第一列置0.

    class Solution {
        public void setZeroes(int[][] matrix) {
            boolean fstLineHas0=false;
            boolean fstColumnHas0=false;
            int xLen=matrix.length,yLen=matrix[0].length;
            for(int i=0;i<xLen;i++){
                if(matrix[i][0]==0){
                    fstColumnHas0=true;
                    break;
                }
            }
            for(int j=0;j<yLen;j++){
                if(matrix[0][j]==0){
                    fstLineHas0=true;
                    break;
                }
            }
            for(int i=1;i<xLen;i++){
                for(int j=1;j<yLen;j++){
                    if(matrix[i][j]==0){
                        matrix[i][0]=0;
                        matrix[0][j]=0;
                    }
                }
            }
            for(int i=1;i<xLen;i++){
                if(matrix[i][0]==0){
                    for(int j=1;j<yLen;j++)matrix[i][j]=0;
                }
            }
            for(int j=1;j<yLen;j++){
                if(matrix[0][j]==0){
                    for(int i=1;i<xLen;i++)matrix[i][j]=0;
                }
            }
            if(fstColumnHas0)for(int i=0;i<xLen;i++)matrix[i][0]=0;
            if(fstLineHas0)for(int j=0;j<yLen;j++)matrix[0][j]=0;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    77. 组合

    给定两个整数 nk,返回范围 [1, n] 中所有可能的 k 个数的组合。

    你可以按 任何顺序 返回答案。

    示例 1:

    输入:n = 4, k = 2
    输出:
    [
    [2,4],
    [3,4],
    [2,3],
    [1,2],
    [1,3],
    [1,4],
    ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    示例 2:

    输入:n = 1, k = 1
    输出:[[1]]
    
    • 1
    • 2

    一开始我就是写了一个简单的递归遍历,自然是超时了。

    public void dfs(List<List<Integer>> resList, int n, int k){
            
            if(k==0)return;
            if(resList.size()==0){
               for(int i=1;i<=n;i++){
                   List<Integer> tempList=new ArrayList<>();
                   tempList.add(i);
                   resList.add(tempList);
               }
               dfs(resList,n,k-1);
            }
            else if(n<resList.get(0).size()+k){
                resList=new ArrayList<>();
                return;
            }
            else {
                int len=resList.size();
                List<Integer> fstList=resList.get(0);
                int max=fstList.get(fstList.size()-1);
                for(int i=0;i<len;i++){
                    
                    for(int j=max+1;j<=n;j++){
                        List<Integer> tempList=new ArrayList<>(resList.get(0));
                        tempList.add(j);
                        resList.add(tempList);
                    }
                    resList.remove(0);
                }
                dfs(resList,n,k-1);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    后来看了官方题解,我没必要这样频繁地存取元素。

    public void dfs1(List<List<Integer>> resList, List<Integer>tempList, int n, int k, int cur){
            
            if(tempList.size()==k){
                resList.add(new ArrayList<>(tempList));
                return;
            }
            else if(tempList.size()+(n-cur+1)<k){
                return;
            }
            else {
                tempList.add(cur);
                dfs1(resList,tempList,n,k,cur+1);
                tempList.remove(tempList.size()-1);
                dfs1(resList,tempList,n,k,cur+1);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    只有一个 tempList 元素在不停地动态变化,我们只需要在其长度到达 k 时复制一份传入 resList 列表即可。

    对于每次遍历的元素,我们选择是保留或忽略此元素,这样从所有元素都保留的结果到所有元素都忽略的结果都能考虑到。

    这种算法叫做剪枝。当当前这个分支已经无法满足题意,或者已经可以判定非最优解了,就可以把这个枝剪掉,不要继续走了。

    78. 子集

    找出给定集合的所有子集(从空集到他自身都要找到)。

    这个和上面的深度遍历是一个道理,区别在于“什么时候是一轮判断完成的标志”。上面一题是 tempList 长度到达 k 固定长度的时候结束并存入本轮循环的 list,此题可以是扫描完全数组时存入。

    class Solution {
        List<List<Integer>> resList=new ArrayList<>();
        List<Integer> tempList=new ArrayList<>();
        public List<List<Integer>> subsets(int[] nums) {
            dfs(0,nums);
            return resList;
        }
    
        public void dfs(int cur, int[] nums){
            if(cur==nums.length){
                resList.add(new ArrayList<>(tempList));
                return;
            }
            tempList.add(nums[cur]);
            dfs(cur+1,nums);
            tempList.remove(tempList.size()-1);
            dfs(cur+1,nums);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    81. 搜索旋转数组 II

    已知存在一个按非降序排列的整数数组 nums ,数组中的值不必互不相同。

    在传递给函数之前,nums 在预先未知的某个下标 k0 <= k < nums.length)上进行了 旋转 ,使数组变为 [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,4,4,5,6,6,7] 在下标 5 处经旋转后可能变为 [4,5,6,6,7,0,1,2,4,4]

    给你 旋转后 的数组 nums 和一个整数 target ,请你编写一个函数来判断给定的目标值是否存在于数组中。如果 nums 中存在这个目标值 target ,则返回 true ,否则返回 false

    你必须尽可能减少整个操作步骤。

    示例 1:

    输入:nums = [2,5,6,0,0,1,2], target = 0
    输出:true
    
    • 1
    • 2

    主要可能出现的问题:形如 [1,1,1,1,3,1,1,1,1,1,1,1,1] 这样的数组,我们不确定特殊数处在什么位置。这样就不能用传统二分法一砍砍一半了。

    class Solution {
        public boolean search(int[] nums, int target) {
            int left=0,right=nums.length-1;
            while(left<=right){
                int mid=(left+right)/2;
                if(nums[mid]==target)return true;
                else if(nums[left]==nums[mid]&&nums[right]==nums[mid]){//头尾缩减一点,来逐步排查特殊数部分
                    left++;
                    right--;
                }
                else if(nums[mid]<=nums[right]){
                    if(nums[mid]<target&&nums[right]>=target)left=mid+1;//右侧自增,且 target 落在右侧区间中
                    else right=mid-1;
                    
                }
                else {
                    if(nums[mid]>target&&nums[left]<=target)right=mid-1;//左侧自增,且 target 落在左侧区间中
                    else left=mid+1;
                }
            }
            return false;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    90. 子集 II

    给你一个整数数组 nums ,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。

    解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。

    示例 1:

    输入:nums = [1,2,2]
    输出:[[],[1],[1,2],[1,2,2],[2],[2,2]]
    
    • 1
    • 2

    和之前的组合总和题有些类似,主要是注意不要重复。

    解法:统一规定,如果此次递归访问的元素和上一次一样,且上一次没有包含这个元素,那么本次递归不再尝试加入此元素。

    class Solution {
        List<List<Integer>> resList=new ArrayList<>();
        List<Integer> tempList=new ArrayList<>();
        public List<List<Integer>> subsetsWithDup(int[] nums) {
            Arrays.sort(nums);
            dfs(nums, 0,false);
            return resList;
        }
    
        public void dfs(int[] nums, int cur,boolean visited){
            if(cur==nums.length){
                resList.add(new ArrayList<Integer>(tempList));
                return;
            }
            dfs(nums, cur+1,false);//可以放弃此元素继续递归
            if(cur>0&&nums[cur-1]==nums[cur]&&(!visited))return;//但是不能使用重复的元素,不然就和上一轮循环重复了。
            tempList.add(nums[cur]);
            dfs(nums, cur+1,true);
            tempList.remove(tempList.size()-1);
            
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    95. 不同的二叉搜索树 II

    给你一个整数 n ,请你生成并返回所有由 n 个节点组成且节点值从 1n 互不相同的不同 二叉搜索树 。可以按 任意顺序 返回答案。

    示例 1:

    img

    输入:n = 3
    输出:[[1,null,2,null,3],[1,null,3,2],[2,1,3],[3,1,null,null,2],[3,2,null,1]]
    
    • 1
    • 2

    还是有规律的。我们每次遍历选取一个节点作为根节点,左边的树递归生成左子树,右边递归生成右子树,两树再合并到根节点左右之下。

    /**
     * Definition for a binary tree node.
     * public class TreeNode {
     *     int val;
     *     TreeNode left;
     *     TreeNode right;
     *     TreeNode() {}
     *     TreeNode(int val) { this.val = val; }
     *     TreeNode(int val, TreeNode left, TreeNode right) {
     *         this.val = val;
     *         this.left = left;
     *         this.right = right;
     *     }
     * }
     */
    class Solution {
        public List<TreeNode> generateTrees(int n) {
            if(n==0)return new LinkedList<TreeNode>();
            return generateTree(1,n);
        }
    
        public List<TreeNode> generateTree(int start, int end){
            List<TreeNode> resList=new LinkedList<>();
            if(start>end){
                resList.add(null);
                return resList;
            };
            for(int i=start;i<=end;i++){
                List<TreeNode> leftTree=generateTree(start,i-1);
                List<TreeNode> rightTree=generateTree(i+1,end);
                for(TreeNode left:leftTree){
                    for(TreeNode right:rightTree){
                        TreeNode root=new TreeNode(i);
                        root.left=left;
                        root.right=right;
                        resList.add(root);
                    }
                }
            }
            return resList;
            
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    105. 从前序与中序遍历序列构造二叉树

    给定两个整数数组 preorderinorder ,其中 preorder 是二叉树的先序遍历inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。

    示例 1:

    img

    输入: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
    输出: [3,9,20,null,null,15,7]
    
    • 1
    • 2

    主要是一个思想。

    preorder:形如 root, x, x, x, x..., x

    inorder:形如 x, x, x, x... root, x, x... x

    那么我们每次找到 root 和左子树右子树,递归继续操作即可。

    class Solution {
        public TreeNode buildTree(int[] preorder, int[] inorder) {
            return f(preorder, inorder, 0,preorder.length-1,0,inorder.length-1);
        }
    
        public TreeNode f(int[] preorder, int[] inorder, int preLeft, int preRight, int inLeft, int inRight){
            if(preLeft>preRight)return null;
            TreeNode root=new TreeNode(preorder[preLeft]);
            int i=inLeft;
            while(inorder[i]!=preorder[preLeft])i++;
            root.left=f(preorder,inorder,preLeft+1,i-inLeft+preLeft,inLeft,i-1);
            root.right=f(preorder,inorder,preLeft+(i-inLeft)+1,preRight,i+1,inRight);
            return root;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    130. 被围绕的区域

    给你一个 m x n 的矩阵 board ,由若干字符 'X''O' ,找到所有被 'X' 围绕的区域,并将这些区域里所有的 'O''X' 填充。

    示例 1:

    img

    输入:board = [["X","X","X","X"],["X","O","O","X"],["X","X","O","X"],["X","O","X","X"]]
    输出:[["X","X","X","X"],["X","X","X","X"],["X","X","X","X"],["X","O","X","X"]]
    解释:被围绕的区间不会存在于边界上,换句话说,任何边界上的 'O' 都不会被填充为 'X'。 任何不在边界上,或不与边界上的 'O' 相连的 'O' 最终都会被填充为 'X'。如果两个元素在水平或垂直方向相邻,则称它们是“相连”的。
    
    • 1
    • 2
    • 3

    解决办法:首先我们扫描一下所有边界,对于其中值 =O 的元素,赋值=A 表示要保留,递归判断其上下左右中出现 O 的地方也作同样处理。

    然后遍历整个数组,把 A 的部分改为 O,把 X O 都改成 X。

    class Solution {
        public void solve(char[][] board) {
            int lineLen=board.length;
            int columnLen=board[0].length;
            for(int i=0;i<lineLen;i++){
                dfs(board,i,0);
                dfs(board,i,columnLen-1);
            }
            for(int i=0;i<columnLen;i++){
                dfs(board,0,i);
                dfs(board,lineLen-1,i);
            }
            for(int i=0;i<lineLen;i++){
                for(int j=0;j<columnLen;j++){
                    if(board[i][j]=='A')board[i][j]='O';
                    else board[i][j]='X';
                }
            }
        }
    
        public void dfs(char[][] board, int i, int j){
            if(i<0||i>=board.length)return;
            if(j<0||j>=board[0].length)return;
            if(board[i][j]=='A'||board[i][j]=='X')return;
            else board[i][j]='A';
            dfs(board,i,j-1);
            dfs(board,i,j+1);
            dfs(board,i-1,j);
            dfs(board,i+1,j);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
  • 相关阅读:
    R语言:多值提取到点
    c++视觉处理----固定阈值操作:Threshold()函数,实时处理:二值化,反二值化,截断,设为零,反向设为零
    基于JAVA社区便民服务系统社区便民服务计算机毕业设计源码+系统+mysql数据库+lw文档+部署
    javafx例子笔记
    element-ui——select多选框远端搜索组件封装
    计算机组成原理--存储系统
    Allegro172版本DFM功能介绍
    vmware的Linux虚拟机创建新的卷组VG
    everything的高效使用方法
    OJ练习第182题——字典树(前缀树)
  • 原文地址:https://blog.csdn.net/jtwqwq/article/details/132749085