代码随想录刷题day2丨209.长度最小的子数组,59.螺旋矩阵II,区间和,开发商购买土地,数组专题总结

代码随想录刷题day2丨209.长度最小的子数组,59.螺旋矩阵II,区间和,开发商购买土地,数组专题总结

1. 题目

1.1长度最小的子数组

  • 题目链接:https://leetcode.cn/problems/minimum-size-subarray-sum/

    在这里插入图片描述

  • 视频讲解:https://www.bilibili.com/video/BV1tZ4y1q7XE

  • 文档讲解:https://programmercarl.com/0209.%E9%95%BF%E5%BA%A6%E6%9C%80%E5%B0%8F%E7%9A%84%E5%AD%90%E6%95%B0%E7%BB%84.html

  • 解题思路:暴力解法或者双指针解法

  • 代码

    • 暴力解法:两个for循环,一个控制起始位置,另一个控制终止位置

      //时间复杂度:O(n^2)
      //空间复杂度:O(1)
      class Solution {
          public int minSubArrayLen(int target, int[] nums) {
              int result = Integer.MAX_VALUE;
              int sum = 0; // 子序列的数值之和
              int subLength = 0; // 子序列的长度
              for (int i = 0; i <= nums.length - 1; i++) { // 设置子序列起点为i
                  sum = 0;
                  for (int j = i; j <= nums.length - 1; j++) { //设置子序列终止位置为j
                      sum += nums[j];
                      if(sum >= target){ // 一旦发现子序列和超过了target,更新result
                          subLength = j - i + 1; // 取子序列的长度
                          result = result < subLength ? result :subLength;
                          break; // 因为我们是找符合条件最短的子序列,所以一旦符合条件就break
                      }
                  }
              }
              // 如果result没有被赋值的话,就返回0,说明没有符合条件的子序列
              return result == Integer.MAX_VALUE ? 0 : result;
          }
      }
      //该题用暴力解法已经超出时间限制
      
    • 双指针解法:一个for循环,里面的j指针指的是终止位置,滑动窗口的精髓就是如何移动起始位置

      在这里插入图片描述

      //时间复杂度:O(n)
      //空间复杂度:O(1)
      class Solution {
          public int minSubArrayLen(int target, int[] nums) {
              int i = 0;//起始位置
              int sum = 0;// 滑动窗口数值之和
              int result = Integer.MAX_VALUE;//取最大可以不断更新subLength
              int subLength = 0;// 滑动窗口的长度
              for (int j = 0; j <= nums.length - 1; j++) {
                  sum += nums[j];
                  while(sum >= target){
                      subLength = j - i + 1;// 取子序列的长度
                      result = result < subLength ? result :subLength;
                      sum = sum - nums[i];// 这里体现出滑动窗口的精髓之处,不断变更i(子序列的起始位置)
                      i++;
                  }
              }
              // 如果result没有被赋值的话,就返回0,说明没有符合条件的子序列
              return result == Integer.MAX_VALUE ? 0 : result;
          }
      }
      
  • 总结

    • 在本题中实现滑动窗口,主要确定如下三点:

      • 窗口是什么?
        • 窗口就是 满足其和 ≥ s 的长度最小的 连续 子数组。
      • 如何移动窗口的起始位置?
        • 窗口的起始位置如何移动:如果当前窗口的值大于等于s了,窗口就要向前移动了(也就是该缩小了)。
      • 如何移动窗口的结束位置?
        • 窗口的结束位置如何移动:窗口的结束位置就是遍历数组的指针,也就是for循环里的索引。
    • 解题的关键在于 窗口的起始位置如何移动,如图所示:result指的是最终我们要取的最小的长度

      在这里插入图片描述

      • 可以发现滑动窗口的精妙之处在于根据当前子序列和大小的情况,不断调节子序列的起始位置。从而将O(n^2)暴力解法降为O(n)。

1.2螺旋矩阵II

  • 题目链接:https://leetcode.cn/problems/spiral-matrix-ii/

    在这里插入图片描述

  • 视频讲解:https://www.bilibili.com/video/BV1SL4y1N7mV/

  • 文档讲解:https://programmercarl.com/0059.%E8%9E%BA%E6%97%8B%E7%9F%A9%E9%98%B5II.html

  • 解题思路:左闭右开 + 循环遍历
    在这里插入图片描述

  • 代码

    //时间复杂度 O(n^2): 模拟遍历二维矩阵的时间
    //空间复杂度 O(1)
    class Solution {
        public int[][] generateMatrix(int n) {
            int startx = 0;//起始行坐标
            int starty = 0;//起始列坐标
            int offset = 1;// // 需要控制每一条边遍历的长度,每次循环右边界收缩一位
            int count = 1;// 矩阵中需要填写的数字
            int loop = 1;// 记录当前的圈数
            int nums[][] = new int[n][n]; 
            int i,j;
            while(loop <= 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--){
                    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;
        }
    }
    
  • 总结

    • 坚持循环不变量原则

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

      • 填充上行从左到右

      • 填充右列从上到下

      • 填充下行从右到左

      • 填充左列从下到上

      • 由外向内一圈一圈这么画下去,这里一圈下来,我们要画每四条边,这四条边怎么画,每画一条边都要坚持一致的左闭右开,或者左开右闭的原则,这样这一圈才能按照统一的规则画下来。

      • 按照左闭右开的原则画一圈:

        在这里插入图片描述

        • 这里每一种颜色,代表一条边,我们遍历的长度,可以看出每一个拐角处的处理规则,拐角处让给新的一条边来继续画。
        • 转圈数等于 n/2

2.区间和

  • 题目

    在这里插入图片描述

  • 解题思路:

    • 最直观的想法:给一个区间,然后 把这个区间的和都累加一遍。(这么做会超时)

      import java.util.Scanner;
      
      public class Main {
          public static void main(String[] args) {
              Scanner scanner = new Scanner(System.in);
              
              // 读取数组长度
              int n = scanner.nextInt();
              int[] vec = new int[n];
              
              // 读取数组元素
              for (int i = 0; i < n; i++) {
                  vec[i] = scanner.nextInt();
              }
              
              // 读取并处理查询
              while (scanner.hasNextInt()) {
                  int a = scanner.nextInt();
                  int b = scanner.nextInt();
                  int sum = 0;
                  
                  // 累加区间 a 到 b 的和
                  for (int i = a; i <= b; i++) {
                      sum += vec[i];
                  }
                  
                  System.out.println(sum);
              }
              
              scanner.close();
          }
      }
      
    • 接下来我们来引入前缀和,看看前缀和如何解决这个问题

      • 前缀和的思想是重复利用计算过的子数组之和,从而降低区间查询需要累加计算的次数。

      • 前缀和 在涉及计算区间和的问题时非常有用

        在这里插入图片描述

      • 如果,我们想统计,在vec数组上 下标 2 到下标 5 之间的累加和,那是不是就用 p[5] - p[1] 就可以了。

        • 特别注意: 在使用前缀和求解的时候,要特别注意 求解区间。
        • 如上图,如果我们要求 区间下标 [2, 5] 的区间和,那么应该是 p[5] - p[1],而不是 p[5] - p[2]。
      • 代码

        import java.util.Scanner;
        
        public class Main {
            public static void main(String[] args) {
                Scanner scanner = new Scanner(System.in);
        
                int n = scanner.nextInt();
                int[] vec = new int[n];
                int[] p = new int[n];
        
                int presum = 0;
                for (int i = 0; i < n; i++) {
                    vec[i] = scanner.nextInt();
                    presum += vec[i];
                    p[i] = presum;
                }
        
                while (scanner.hasNextInt()) {
                    int a = scanner.nextInt();
                    int b = scanner.nextInt();
        
                    int sum;
                    if (a == 0) {
                        sum = p[b];
                    } else {
                        sum = p[b] - p[a - 1];
                    }
                    System.out.println(sum);
                }
        
                scanner.close();
            }
        }
        

3.开发商购买土地(后面再补)

  • 文档链接:https://www.programmercarl.com/kamacoder/0044.%E5%BC%80%E5%8F%91%E5%95%86%E8%B4%AD%E4%B9%B0%E5%9C%9F%E5%9C%B0.html

4.数组专题总结

  • 数组是存放在连续内存空间上的相同类型数据的集合。

  • 数组下标都是从0开始的。

  • 数组内存空间的地址是连续的

  • 数组的元素是不能删的,只能覆盖。

  • 数组的经典题目

    • 二分法:循环不变量原则

      • 暴力解法时间复杂度:O(n)
      • 二分法时间复杂度:O(logn)
    • 双指针法(快慢指针法):通过一个快指针和慢指针在一个for循环下完成两个for循环的工作。

      • 暴力解法时间复杂度:O(n^2)
      • 双指针时间复杂度:O(n)
    • 滑动窗口:滑动窗口的精妙之处在于根据当前子序列和大小的情况,不断调节子序列的起始位置。从而将O(n^2)的暴力解法降为O(n)。

      • 主要要理解滑动窗口如何移动 窗口起始位置,达到动态更新窗口大小的,从而得出长度最小的符合条件的长度。

      • 暴力解法时间复杂度:O(n^2)

      • 滑动窗口时间复杂度:O(n)

    • 螺旋矩阵II这种模拟类题目用到了:循环不变量原则

通过一个快指针和慢指针在一个for循环下完成两个for循环的工作。**

- 暴力解法时间复杂度:O(n^2)
- 双指针时间复杂度:O(n)
  • 滑动窗口:滑动窗口的精妙之处在于根据当前子序列和大小的情况,不断调节子序列的起始位置。从而将O(n^2)的暴力解法降为O(n)。

    • 主要要理解滑动窗口如何移动 窗口起始位置,达到动态更新窗口大小的,从而得出长度最小的符合条件的长度。

    • 暴力解法时间复杂度:O(n^2)

    • 滑动窗口时间复杂度:O(n)

  • 螺旋矩阵II这种模拟类题目用到了:循环不变量原则

  • 前缀和:前缀和的思路很简单,但非常实用

  • 16
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值