代码随想录算法训练营第二天|997.有序数组的平方、209长度最小的子数组、59.螺旋矩阵

164 阅读5分钟

997.有序数组的平方

题目链接

解题方法:双指针,一个for循环

分析题意:

  1. 题目给定数组是一个有序的数组
  2. 要返回每个数的平方,那么新数组中的最大值一定是在数组的两端

解题步骤:

  1. 定义左右两个指针(i,j)分别指向数组的前后两端

  2. 定义一个指针(k)指向新数组的末尾

  3. 开始遍历,结束条件为 i <= j这里的 = 需要思考,如果没有 = 的话就会把 i = j 的那个数漏掉

    • 如果 下标为 i 的值的平方 > 下标为 j 的值的平方

      • 将 下标为 i 的值的平方赋给下标为 K 的值
      • i 向后移动一位,k 向前移动一位
    • 如果 下标为 i 的值的平方 <= 下标为 j 的值的平方(这里也需要思考 = 的情况,相等时说明 i , j 指向的值的平方相等,所以可以直接归到这一种情况中

      • 将 下标为 j 的值的平方赋给下标为 K 的值
      • j 向前移动一位,k 向前移动一位

图示(图片来源:代码随想录)

977.有序数组的平方.gif

解题代码:

class Solution {
    public int[] sortedSquares(int[] nums) {
        //定义新数组,长度等于原数组
        int[] result = new int[nums.length];
        //定义指针指向新数组的末尾
        int k = result.length - 1;
        for(int i = 0,j = nums.length - 1;i <= j;){
            if(nums[i] * nums[i] > nums[j] * nums[j]){
                result[k] = nums[i] * nums[i];
                i++;
                k--;
            }else{
                result[k] = nums[j] * nums[j];
                j--;
                k--;
            }
        }
        return result;
    }
}

209长度最小的子数组(滑动窗口)

题目链接

解题思路

双指针,一个for循环

图片来源:代码随想录

209.长度最小的子数组.gif

需要理解的点

**窗口:**满足其和 ≥ s 的长度最小的 连续 子数组

**窗口起始位置移动:**当前窗口的值大于s,窗口就要向前移动(缩小窗口)

**窗口结束位置移动:**窗口的结束位置就是遍历数组的指针,也就是for循环里的索引

对应步骤:

  1. for循环中变量是表示 的 滑动窗口的终止位置

  2. 计算当前窗口中元素值总和(sum)

  3. while循环判断当前窗口元素值的总和是否大于s while(sum>=s)

  4. 获取当前窗口的长度(subLength = 终止位置 - 初始位置 + 1)

  5. 将最终需要返回的结果resultsubLength比较取更小的一个赋值给result

    result初始默认值为:Integer.MAX_VALUE

  6. 窗口起始位置移动,计算新窗口的元素值总和 (容易忽略)

  7. 窗口起始位置+1

实现代码

class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        if(nums==null || nums.length==0){
            return 0;
        }
        int i = 0; //初始位置
        int totalNum = 0;
        int result = Integer.MAX_VALUE;
        for(int j = 0;j < nums.length;j++){
            totalNum += nums[j];
            while(totalNum >= target){
                int subLength = j - i + 1; //计算当前窗口的长度
                result = Math.min(result,subLength);//比较当前数组长度和最大值,返回给result
                totalNum -= nums[i];//初始位置移动后需要更新数组和
                i++;//初始位置向前移动
            }
        }
        return result == Integer.MAX_VALUE ? 0 : result;
    }
}s

两个注意

1.为什么用 while 不用 if ( 很重要!!!)

因为窗口是需要不断移动的,并不是只判断一次就完事了

2.为什么时间复杂度是O(n)

不能以为for里放一个while就以为是O(n^2), 主要看每一个元素被操作的次数,每个元素在滑动窗后进来操作一次,出去操作一次,每个元素都是被操作两次,所以时间复杂度是 2 × n 也就是O(n)。

59.螺旋矩阵Ⅱ

题目链接

解题思路:

图片来源:代码随想录

20220922102236.png

求解本题要坚持循环不变量原则,同时通过横纵坐标定位。

模拟顺时针画矩阵的过程:

  • 填充上侧从左到右
  • 填充右测从上到下
  • 填充下侧从右到左
  • 填充左侧从下到上

由外向内一圈一圈这么画下去。

要画四条边,每画一条边都要坚持一致的左闭右开,或者左开右闭的原则,一圈按照统一的规则画下来。

解题时的想法:

  1. 应该循环多少次才能把矩阵赋值完成呢?如果 n = 4 ,那么则需要 n/2 = 2 遍才能赋值完成,如果 n 是奇数的话还需要另外考虑最中间的赋值

  2. 对每条边的处理应该使用相同的规则(左闭右开),以及如何确定遍历的最终结束条件(也就是遍历到哪里 利用偏移量offset计算)

  3. 遍历完一圈后,下一次遍历的起始位置以及偏移量都会发生变化

解题代码:

class Solution {
    public int[][] generateMatrix(int n) {

      int[][] nums = new int[n][n];
      int loop = 0; //循环的次数
      int start = 0; //循环开始的点
      int offset = 1;//偏移量
      int count = 1; //定义填充的数字
      int i,j;
      while(loop++ < n/2){
        //上侧从左到右
        for(j = start;j < n - offset;j++){
          nums[start][j] = count++;
        }
        //右侧从上到下
        for(i = start;i < n - offset;i++){
          nums[i][j] = count++;
        }
        //下侧从右到左
        for(;j > start;j--){
          nums[i][j] = count++;
        }
        //左侧从下到上
        for(;i > start;i--){
          nums[i][j] = count++;
        }
        start++;
        offset++;
      }
      if(n % 2 == 1){
        nums[start][start] = count;
      }
      return nums;
    }
}

个人小结: 感觉数组的题目不是特别难,主要需要理解几个重要的思想,一个是二分法和螺旋矩阵中的循环不变量,确定区间;另一个就是利用双指针思想解决移除元素和滑动窗口,刷完后感觉研究出这些算法的佬太牛了,自己还需要努力,脚踏实地的一步步往前走。