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

209.长度最小的子数组

找出该数组中满足其总和大于等于 target 的长度最小的 子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度**。**如果不存在符合条件的子数组,返回 0

class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        int fast = 0; //快指针
        int slow = 0;//慢指针
        int sum = 0;//区间和
        int length = Integer.MAX_VALUE;//子数列长度
        while(fast < nums.length) {//防止快指针越界
            sum += nums[fast];  //计算区间和
            while (sum >= target) {//区间和大于target时记录最小长度
                length = Math.min(length, fast - slow + 1);
                sum -= nums[slow++];  //删除左边界元素,继续向右寻找
            }
            fast++;//快指针右移
        }
        return length == Integer.MAX_VALUE ? 0 : length;
    }
}
  • 关于length的值:初始化时应初始化为最大值,否则当数组足够大时,length == Integer.MAX_VALUE ?值为true,方法返回0

  • 优化:第一层while()循环可以改进为for循环,代码更简洁易读

for(int fast = 0; fast < nums.length; fast++) {
     sum += nums[fast];
   	...
}

59.螺旋矩阵 2

给你一个正整数 n ,生成一个包含 1n2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix

class Solution {
    public int[][] generateMatrix(int n) {
        int[][] matix = new int[n][n];
        int startRow = 0;
        int startColumn = 0;
        int loop = n / 2;
        int num = 1;
        int length = n - 1;
        if(n == 1) { //n = 1时loop为0,采用if语句特殊处理
            matix[n / 2][n / 2] = num;
        }
        while(loop > 0) {
            int row = startRow;
            int col = startColumn;
            //顶行
            for(; col < length; col++) {
                matix[row][col] = num++;
            }
            //右列
            for(; row < length; row++) {
                matix[row][col] = num++;
            }
            //下行
            for(; col > startColumn; col--) {
                matix[row][col] = num++;
            }
            //左列
            for(; row > startRow; row--) {
                matix[row][col] = num++;
            }
            //n为奇数时单独处理矩阵中心
            if(n % 2 == 1) {
                matix[n / 2][n / 2] = num;
            }
            startRow++;
            startColumn++;
            loop--;
            length--;
        }
        return matix;
    }
}
  • 核心思路:

    ​ 控制每次插入数据的长度相等,可以按照相同条件统一循环处理

58.区间和

给定一个整数数组 Array,请计算该数组在每个指定区间内元素的总和。

题目链接

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        int n = input.nextInt();
        int[] array = new int[n];//记录输入数组
        int[] sumArray = new int[n];//记录前缀和数组
        int presum = 0;//计算前缀和
        for(int i = 0; i < n; i++) {
            array[i] = input.nextInt();
            presum += array[i];
            sumArray[i] = presum;//保存前缀和在数组中
        }
        while(input.hasNextInt()) {
            int a = input.nextInt();//区间头
            int b = input.nextInt();//区间尾
            int output = 0;
            if(a == 0) {//a为0,区间和即为前缀和数组中索引b处储存的值
                output = sumArray[b];
            }else {//a不为0,区间和即为索引b处的值减去索引a-1处的值
                output = sumArray[b] - sumArray[a - 1];
            }

            System.out.println(output);
        }
        input.close();
    }
}
  • 前缀和

    • 重复利用计算过的子数组之和,从而降低区间查询需要累加计算的次数。
    • 前缀和索引i处保存的值,为原数组索引0到索引i处的累加值
    • 所以求区间和为sumArray[b] - sumArray[a - 1]
    • 若计算sumArray[b] - sumArray[a]则少计算了array[a]
  • 随笔

    前缀和思想和JIT有异曲同工之妙

    • 前缀和通过预处理保存区间和,避免重复计算将后续查询的时间复杂度降为O(1),
    • JIT通过动态编译热点代码,保存第一次编译后的机械码,避免重复解释执行同一段字节码

44.开发商购买土地

在一个城市区域内,被划分成了n * m个连续的区块,每个区块都拥有不同的权值,代表着其土地价值。目前,有两家开发公司,A 公司和 B 公司,希望购买这个城市区域的土地。

现在,需要将这个城市区域的所有区块分配给 A 公司和 B 公司。

然而,由于城市规划的限制,只允许将区域按横向或纵向划分成两个子区域,而且每个子区域都必须包含一个或多个区块。 为了确保公平竞争,你需要找到一种分配方式,使得 A 公司和 B 公司各自的子区域内的土地总价值之差最小。

注意:区块不可再分。

题目链接

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        int n = input.nextInt();
        int m = input.nextInt();
        int[][] area = new int[n][m];
        int sum = 0;
        for(int i = 0; i < n; i++) {
            for(int j = 0; j < m; j++) {
                area[i][j] = input.nextInt();
                sum += area[i][j];
            }
        }
        //横向前缀和
        int[] rowPresum = new int[n];
        for(int i = 0; i < n; i++) {
            for(int j = 0; j < m; j++) {
                rowPresum[i] += area[i][j];
            }
        }
        //纵向前缀和
        int[] columnPresum = new int[m];
        for(int j = 0; j < m; j++) {
            for(int i = 0; i < n; i++) {
                columnPresum[j] += area[i][j];
            }
        }
        
        int result = Integer.MAX_VALUE;
        int rowPresumCut = 0;//当前行和
        for(int i = 0; i < n; i++) {//遍历计算行划分的情况
            rowPresumCut += rowPresum[i];
            result = Math.min(result, Math.abs((sum - rowPresumCut) - rowPresumCut));//(sum - rowPresumCut)为剩余行和
        }
        int columnPresumCut = 0;//当前列和
        for(int j = 0; j < m; j++) {//遍历计算列划分的情况
            columnPresumCut += columnPresum[j];
            result = Math.min(result, Math.abs((sum - columnPresumCut) - columnPresumCut));
        }
        System.out.println(result);
        input.close();
    }
}
  • 通过前缀和,将二维数组压缩为两个一维数组,分别计算行划分和列划分的情况

  • 核心思路:通过前缀和简化问题

你可能感兴趣的:(算法,矩阵,线性代数,leetcode,java)