代码随想录算法训练营第二天| 209.长度最小的子数组、59.螺旋矩阵II、58. 区间和、44. 开发商购买土地

209.长度最小的子数组--滑动窗口

题目:209. 长度最小的子数组 - 力扣(LeetCode)

讲解:拿下滑动窗口! | LeetCode 209 长度最小的子数组_哔哩哔哩_bilibili

思路一:暴力两层循环

class Solution {
public:
    int minSubArrayLen(int s, vector& nums) {
        int result = INT32_MAX; // 最终的结果
        int sum = 0; // 子序列的数值之和
        int subLength = 0; // 子序列的长度
        for (int i = 0; i < nums.size(); i++) { // 设置子序列起点为i
            sum = 0;
            for (int j = i; j < nums.size(); j++) { // 设置子序列终止位置为j
                sum += nums[j];
                if (sum >= s) { // 一旦发现子序列和超过了s,更新result
                    subLength = j - i + 1; // 取子序列的长度
                    result = result < subLength ? result : subLength;
                    break; // 因为我们是找符合条件最短的子序列,所以一旦符合条件就break
                }
            }
        }
        // 如果result没有被赋值的话,就返回0,说明没有符合条件的子序列
        return result == INT32_MAX ? 0 : result;
    }
};

  • 时间复杂度:O(n^2)
  • 空间复杂度:O(1)

后面力扣更新了数据,暴力解法已经超时了。

思路二:滑动窗口

所谓滑动窗口,就是不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果

滑动窗口的本质是双指针,一个指针控制左区间,一个指针控制右区间

用一层for循环代替两层,这个循环中的j是终止位置

滑动窗口的精华:如何移动起始位置

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

  • 窗口内是什么?
  • 如何移动窗口的起始位置?
  • 如何移动窗口的结束位置?

窗口就是 满足其和 ≥ s 的长度最小的 连续 子数组。

窗口的起始位置如何移动:如果当前窗口的值大于等于s了,窗口就要向前移动了(也就是该缩小了)。

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

第一遍错误代码:错误原因:Integer拼写错误;没有考虑不满足要求的情况

public int minSubArrayLen(int target, int[] nums) {
        int i=0,j=0; //起始、终止位置
        int result = Integer.MAX_VALUE; 
        int sum = 0;
        int subLength = 0;
        for( ; j= target){
                result = Math.min(subLength,result);
                sum -= nums[i];  //移动滑动窗口起始位置
                i++;
                subLength = j-i+1;
            }
        }
        return result;
    }

解决:加一个flag标志位判断是否有满足条件的情况

或者在最后返回时判断

 return result == Integer.MAX_VALUE ? 0 : result;
class Solution {
     public int minSubArrayLen(int target, int[] nums) {
        int i=0,j=0; //起始、终止位置
        int result = Integer.MAX_VALUE; 
        int sum = 0;
        int subLength = 0;
        boolean flag = false;
        for( ; j= target){
                flag = true;
                result = Math.min(subLength,result);
                sum -= nums[i];  //移动滑动窗口起始位置
                i++;
                subLength = j-i+1;
            }
        }
        if(flag)return result;
        else return 0;
    }
}
  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

一些录友会疑惑为什么时间复杂度是O(n)

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

补充:java中基本数据类型的最大值

public class PrimitiveTypeMaxValues {
    public static void main(String[] args) {
        System.out.println("byte 最大值: " + Byte.MAX_VALUE);      // 输出: 127
        System.out.println("short 最大值: " + Short.MAX_VALUE);    // 输出: 32767
        System.out.println("int 最大值: " + Integer.MAX_VALUE);    // 输出: 2147483647
        System.out.println("long 最大值: " + Long.MAX_VALUE);      // 输出: 9223372036854775807
        System.out.println("float 最大值: " + Float.MAX_VALUE);    // 输出: 3.4028235E38
        System.out.println("double 最大值: " + Double.MAX_VALUE);  // 输出: 1.7976931348623157E308
    }
}
  • 自定义最大值时,推荐使用final关键字将其定义为常量。
  • 对于集合或数组中的最大值,可使用 Stream API 或者Collections.max()方法来获取。
  • 若要对对象属性进行最大值限制,可在类中添加验证逻辑。

59.螺旋矩阵II

题目:

59. 螺旋矩阵 IIj

讲解:一入循环深似海 | LeetCode:59.螺旋矩阵II_哔哩哔哩_bilibili

给定一个正整数 n,生成一个包含 1 到 n^2 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵。

示例:

输入: 3 输出: [ [ 1, 2, 3 ], [ 8, 9, 4 ], [ 7, 6, 5 ] ]

这道题目可以说在面试中出现频率较高的题目,转圈:边界控制,模拟过程,但却十分考察对代码的掌控能力。

如果要写出正确的二分法一定要坚持循环不变量原则

而求解本题依然是要坚持循环不变量原则。

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

  • 填充上行从左到右
  • 填充右列从上到下
  • 填充下行从右到左
  • 填充左列从下到上

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

代码随想录算法训练营第二天| 209.长度最小的子数组、59.螺旋矩阵II、58. 区间和、44. 开发商购买土地_第1张图片

class Solution {
    public int[][] generateMatrix(int n) {
         int count = 1;
        int startX = 0,startY = 0;
        int offset = 1;
        int i,j;
        int nums[][] = new int[n][n];
        for (int rotate = 1; rotate <= n/2; rotate++){
            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++;
        }
        if (n%2==1){  //奇数
            nums[n/2][n/2] = count++;
        }
        return nums;
    }
}

58. 区间和

58. 区间和(第九期模拟笔试)

数组常用解题技巧:前缀和

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

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

例如,我们要统计 vec[i] 这个数组上的区间和。

我们先做累加,即 p[i] 表示 下标 0 到 i 的 vec[i] 累加 之和。

如图:

代码随想录算法训练营第二天| 209.长度最小的子数组、59.螺旋矩阵II、58. 区间和、44. 开发商购买土地_第2张图片

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

第一次用ACM输入输出模式,并且尝试使用纸笔计算模拟

1. import的地方要注意

2. public class Main{}    不能加()

3. 通过 sc.hasNextInt()来控制循环条件

4. 最后记得关闭scanner

import java.util.Scanner;

public class Main{
    public static void main(String args[]){
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int[] arr = new int[n];
        int[] sum = new int[n];

        arr[0] = sc.nextInt();
        sum[0] = arr[0];

        for(int i=1;i

44. 开发商购买土地

前缀和

这个还没过,下次看

44. 开发商购买土地(第五期模拟笔试)

看到本题,大家如果想暴力求解,应该是 n^3 的时间复杂度,

一个 for 枚举分割线, 嵌套 两个for 去累加区间里的和。

如果本题要求 任何两个行(或者列)之间的数值总和,大家在0058.区间和 的基础上 应该知道怎么求。

就是前缀和的思路,先统计好,前n行的和 q[n],如果要求矩阵 a行 到 b行 之间的总和,那么就 q[b] - q[a - 1]就好。

至于为什么是 a - 1,大家去看 0058.区间和 的分析,使用 前缀和 要注意 区间左右边的开闭情况。

本题也可以使用 前缀和的思路来求解,先将 行方向,和 列方向的和求出来,这样可以方便知道 划分的两个区间的和。

import java.util.Scanner;

public class Main{
    public static void main(String args[]){
        Scanner sc= new Scanner(System.in);
        int n = sc.nextInt();
        int m = sc.nextInt();
        int subSum = 0;
        int totalSum = 0; //所有元素的总和
        int arr[][] = new int[n][m];
        int row[] = new int[n];
        int col[] = new int[m];

        int result = Integer.MAX_VALUE;
        for(int i = 0; i < m; i++){
            arr[0][i] = sc.nextInt();
            subSum += arr[0][i];
        }
        row[0] = subSum;
        totalSum += row[0];
        for(int i = 1; i < n; i++){
            subSum = 0;
            for(int j = 0; j < m; j++){
                arr[i][j] = sc.nextInt();
                subSum += arr[i][j];
            }
            row[i] = row[i-1] + subSum;
            totalSum += row[i];
        }

        subSum=0;
        //求col
        for(int i = 0; i < n; i++){
            subSum += arr[i][0];
        }
        col[0] = subSum;
        result = Math.min(result, Math.abs( 2 * col[0] - totalSum));

        for(int j = 1; j < m; j++){
            subSum = 0;
            for(int i = 0; i < n; i++){
                subSum += arr[i][j];
            }
            col[i] = col[i-1] + subSum;
            //直接进行比较
            result = Math.min(result, Math.abs( 2 * col[i] - totalSum));
        }

        for(int i = 0; i < n; i++){
            result = Math.min(result, Math.abs( 2 * col[i] - totalSum));
        }

        System.out.println(result);
        sc.close();
    }
}

你可能感兴趣的:(代码随想录-算法训练营,算法)