随想录训练营Day2|977.有序数组的平方 ,209.长度最小的子数组 ,59.螺旋矩阵II

737 阅读3分钟

随想录训练营Day2|977.有序数组的平方 ,209.长度最小的子数组 ,59.螺旋矩阵II

标签: LeetCode闯关记


##1. 977.有序数组的平方## ###1.1 题目 相关链接 977.有序数组的平方

###1.2 解题思路 双指针 ###1.3 遇到问题

//错误写法
class Solution {
    public int[] sortedSquares(int[] nums) {
        //运用前后双指针
        int left = 0;
        int right = nums.length - 1;
        int i = nums.length - 1;
        int[] result = new int[nums.length];
        while(left <= right){
        //错误地运用了两个if判断语句,出现The ArrayIndexOutOfBoundsException 报错
            if(nums[left] * nums[left] >= nums[right] * nums[right]){
                result[i--] = nums[left] * nums[left];
                left++;
            }
            if(nums[left] * nums[left] < nums[right] * nums[right]){
                result[i--] =  nums[right] * nums[right];
                right++;
            }
        }
        return result;
    }
}

报错原因分析: 可能出现一个while循环内同时进行两个分支. 如,输入数组 nums 为 [-3, -1, -2],按照你的算法执行下来,执行到 left = 0, right = 2, i = 2 时,应该进行第一个 if 分支,将 -3 的平方存入结果数组 result 的最后一位(即 result[2] = 9),此时指针 left 移动到 1,指针 i 应该减一变成 1。

但是,由于代码中后面使用了两个 if 分支,当指针 left 移动到 1 后,执行了第二个 if 分支,将 (-2) 的平方存入了 result 的倒数第二位(即 result[1] = 4),同时指针 right 又加了一,此时 right 超出了数组的范围,导致越界错误。

因此,代码中应该使用 else if 来确保只会进入一个分支,而不会同时进入两个分支

###1.4 算法实现 代码实现:

 class Solution {
    public int[] sortedSquares(int[] nums) {
        //运用前后双指针
        int left = 0;
        int right = nums.length - 1;
        int i = nums.length - 1;
        int[] result = new int[nums.length];
        while(left <= right){//注意 '=';
            if(nums[left] * nums[left] >= nums[right] * nums[right]){
                result[i--] = nums[left] * nums[left];
                left++;
            }else{注意一个分支: if-else
                result[i--] =  nums[right] * nums[right];
                right--;
            }
        }
        return result;
    }
}

注意点: while(left <= right) 中为"<=",不要遗漏等号,否则最小平方数没有被计入result数组.如图:

###1.5 题目总结 解题耗时:1h ###1.6 相关题目 暂无


##2. 209.长度最小的子数组 ###2.1 题目 209.长度最小的子数组 讲解视频:很清楚 www.bilibili.com/video/BV1tZ…

###2.2 解题思路 1.暴力揭解法:两次for循环 2.运用滑动窗口 ###2.4 实现代码

class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        //运用滑动窗口(双指针)

        int i = 0;//滑动窗口的起始位置;
        int sum = 0; //滑动窗口内的值;
        int result = Integer.MAX_VALUE;//这里怎么取值? 取整数最大值(见注意点)
        for(int j = 0; j < nums.length; j++){//j为滑动窗口的终止位置
            //首先移动j,找到第一次sum>= target的范围;
            sum += nums[j];
            while(sum >= target){//注意这里是while,不是if, 因为并不一定只比较一次就找到最短集合;eg:{1,1,1,1,100},target为100; 所以while能够保证持续向后移动start指针,从而找到正确结果;
                int sum_length = j - i + 1;
                result = Math.min(result, sum_length);//取满足条件的长度最小的子数组
                sum -= nums[i];//start指针后移
                i++;
            }
        }
        return result == Integer.MAX_VALUE ? 0 : result;
    }
}

注意点:

  1. 理解为什么for循环的j必须是滑动窗口的终止位置而不是起始位置;
  2. result怎么取值: Integer.MAX_VALUE represents the maximum positive integer value that can be represented in 32 bits (i.e., 2147483647). This means that no number of type Integer that is greater than 2147483647 can exist in Java.
  3. 为什么用while instead of if;
  4. 最后返回的结果必须做一个判断,排除没有满足条件的额子数组 这一可能性;

###2.5 题目总结 解题耗时: 40min ###2.6 相关题目 904.水果成篮(opens new window) 76.最小覆盖子串(opens new window)


##3. 59.螺旋矩阵II ###3.1 题目 59.螺旋矩阵II 文章讲解:programmercarl.com/0059.%E8%9E… 视频讲解:www.bilibili.com/video/BV1SL…

###3.2 解题思路 初始值: int i = 0; (行数) int j = 0; (列数) int offset = 1; ...

(1)确定圈数(loop):n/2(向下取整),若为奇数,最后一个值单独赋值; (2) 确定统一的遍历规则: 左闭右开; ①上行:左到右②...③..④...

###3.4 实现代码

class Solution {
    public int[][] generateMatrix(int n) {
    int [][] nums = new int[n][n];
    int i = 0; //(行数)
    int j = 0; //(列数)
    int startX = 0;//循环的起始位置(行);
    int startY = 0;//循环的起始位置(列);
    int offset = 1; // n - offset 为上行遍历的终止位置; 
    int loop = 1;//表示循环的圈数;
    int count = 1;//计数赋值;
    while (loop <= n/2){//n/2(向下取整),若为奇数,最后一个值单独赋值;

        //上-行:从左到右(行不变,列变化)
         for(j = startY; j < n - offset; j++){
            nums[startX][j] =count++;
         }
        //右-列:从上到下(列不变,行变化)
         for(i = startX; i < n - offset; i++){
             nums[i][j] = count++;
         }

        //下-行:从右到左(行不变,列变化)
         for(; j > startY; j--){ //注意,这里的j的取值范围? 没看懂代码随想录的取值
             nums[i][j] = count++;
         }
        //左-列:从下到上(列不变,行变化)
        for(; i > startX;i--){
            nums[i][j] = count++;
        }

        //完成一圈遍历之后
        startX++;
        startY++;
        offset++;
        loop++;
    }
    //若n为奇数,矩阵最中间的一个(即需要赋值的最后一个)
    if(n % 2 == 1){
        nums[startX][startY] = count;
    }
    return nums;
    }
}

key point: 统一遍历规则; 优化点: ①用 int start 一个变量 // 每次循环的开始点(start, start) 代替 startX, startY 两个变量 ②因为loop和offset值相同,做相同变化,所以可以只保留loop; ###3.5 题目总结 解题耗时: 40min 看视频讲解的时候,不理解最后一步:为奇数数组的时候,为什么最后直接 nums[i][j]表示矩阵最中间的那个元素,可以被直接赋值. 视频讲错了: 应该是 nums[n/2, n/2] 或者如我代码所写; ###3.6 相关题目 待做: 54.螺旋矩阵 剑指Offer 29.顺时针打印矩阵


##4. 今日心得 双指针真的很有用! 还是很认真在理解! (模拟行为)螺旋指针其实对我来说更多是初始变量太多,搞得我很晕! 没有做相关题目,尽量之后再做一下. 受不了了这格式咋回事?