代码随想录算法训练营Day02

长度最小的子数组

暴力解法和滑动窗口的思想

  1. 暴力解法
class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        int minLength = Integer.MAX_VALUE;
        for (int i = 0; i < nums.length; i++) { // 起始位置
            int sum = 0;
            for (int j = i; j < nums.length; j++) { // 向后遍历找到结束位置
                sum += nums[j];
                if (sum >= target) {
                    minLength = Math.min(minLength, j - i + 1);
                    break; // 找到一个满足条件的子数组后,跳出内层循环,继续检查下一个起始位置
                }
            }
        }
        return minLength == Integer.MAX_VALUE ? 0 : minLength;
    }
}
  1. 滑动窗口

当窗口不满足要求时,end指针向右拓宽边界,满足要求时,start指针向右缩小边界

class Solution {
	public int minSubArrayLen(int target, int[] nums) {
		int start = 0;
		int sum = 0;
		int result = Integer.MAX_VALUE;
		for (int end = 0; end < nums.length; end++) {// 这个end是结束的位置,和前面的暴力解法区分
			sum += nums[end];
			while (sum >= target) {
				int length = end - start + 1;//为什么加1?假设指向同一个元素,子数组的长度为1
				sum = sum - nums[start++];
				result = result <= length ? result : length;
			}
		}
		return result==Integer.MAX_VALUE?0:result;
	}
}

螺旋矩阵

控制好好循环条件即可,搞懂参数表示什么
一条边的最后一个元素,是当前边处理,还是作为下一条边的第一个元素来处理?应该是左闭右开,否则无法统一循环条件

class Solution {
	public int[][] generateMatrix(int n) {
		int startX = 0;
		int startY = 0;//i和j的起始位置
		int offset = 1;//左闭右开,所以需要一个这样的变量来控制边界
		int count = 1;//存入数组的数据
		int loop = 1;//循环的轮数,画图找规律,需要循环n/2次
		int[][] nums = new int[n][n];
		while(loop<=n/2){
			for(int j = startY;j<n-offset;j++){
				nums[startX][j] = count++;
			}
			for(int i = startX;i<n-offset;i++){
				nums[i][n-offset] = count++;
			}
			for(int j = n-offset;j>startY;j--){
				nums[n-offset][j] = count++;
			}
			for(int i = n-offset;i>startX;i--){
				nums[i][startY] = count++;
			}
			startX++;
			startY++;
			loop++;
			offset++;
		}
		if(n%2 != 0){
			nums[n/2][n/2] = n*n;
		}
		return nums;
	}
}

区间和

给定一个整数数组 Array,请计算该数组在每个指定区间内元素的总和。
第一行输入为整数数组 Array 的长度 n,接下来 n 行,每行一个整数,表示数组的元素。随后的输入为需要计算总和的区间,直至文件结束。
输出每个指定区间内元素的总和。

  1. 暴力解法
public class Solution {  
    public static void main(String[] args) {  
        Scanner sc = new Scanner(System.in);  
        int n = sc.nextInt();  
        int[] nums = new int[n];  
        for(int i = 0;i<n;i++){  
            nums[i] = sc.nextInt();  
        }  
        int a = sc.nextInt();  
        int b = sc.nextInt();  
        while(a<=b){  
            int sum = 0;  
            for(int i = a;i<=b;i++){  
                sum+=nums[i];  
            }  
            System.out.println(sum);  
            a = sc.nextInt();  
            b = sc.nextInt();  
        }  
  
  
    }
  1. 前缀和
public class Solution {  
    public static void main(String[] args) {  
        Scanner sc = new Scanner(System.in);  
        int n = sc.nextInt();  
        int[] nums = new int[n];  
        for(int i = 0;i<n;i++){  
            nums[i] = sc.nextInt();  
        }  
        //下面的数组也可以和上面合成一步
        int[] sums = new int[n];  
        sums[0] = nums[0];  
        for(int i = 1;i<n;i++){  
            sums[i] = sums[i-1] + nums[i];  
        }  
        int a = sc.nextInt();  
        int b = sc.nextInt();  
        while(a<=b){  
            int sum = a==0?sums[b]:sums[b]-sums[a-1];//注意这里的a-1  
            System.out.println(sum);  
            a = sc.nextInt();  
            b = sc.nextInt();  
        }  
    }  
}

开发商分配土地

将带权重的矩阵分成两部分,使得两部分权重的差尽可能的小
也是考察前缀和,这里要分清行和列,行用i表示,列用j表示

  1. 前缀和
public class Solution3 {  
    public static void main(String[] args) {  
        Scanner sc = new Scanner(System.in);  
        int n = sc.nextInt();  
        int m = sc.nextInt();  
        int sum = 0;  
        int result = Integer.MAX_VALUE;  
        int[][] nums = new int[n][m];  
        for(int i = 0; i < n; i++){  
            for(int j = 0; j < m; j++){  
                nums[i][j] = sc.nextInt();  
                sum+=nums[i][j];  
            }  
        }  
  
        int[] horizontal = new int[n];//每一行的元素之和  
        int[] vertical = new int[m];//每一列的元素之和  
  
        for(int i = 0; i < n; i++){  
            for(int j = 0; j < m; j++){  
                horizontal[i] += nums[i][j];  
            }  
        }  
        for(int j = 0; j < m; j++){  
            for(int i = 0; i < n; i++){  
                vertical[j] += nums[i][j];  
            }  
        }  
  
        //上面的代码构建了矩阵还有前缀和所需要的sums数组  
        int horizontalCut = 0;//这个表示前面i行的和  
        for(int i = 0; i < n; i++){  
            horizontalCut += horizontal[i];  
            result = Math.min(result, Math.abs((sum-horizontalCut)-horizontalCut));  
        }  
        int verticalCut = 0;  
        for(int j = 0; j < m; j++){  
            verticalCut +=vertical[j];  
            result = Math.min(result, Math.abs((sum-verticalCut)-verticalCut));  
        }  
        System.out.println(result);  
    }  
}
  1. 暴力算法
public static int minLandValueDifference(int n, int m, int[][] grid) {  
    int totalValue = 0;  
  
    // 计算总土地价值  
    for (int i = 0; i < n; i++) {  
        for (int j = 0; j < m; j++) {  
            totalValue += grid[i][j];  
        }  
    }  
  
    int minDiff = Integer.MAX_VALUE;  
  
    // 枚举横向分割线  
    for (int row = 0; row < n - 1; row++) { // 分割线在第 row 行下方  
        int topValue = 0;  
        for (int i = 0; i <= row; i++) {  
            for (int j = 0; j < m; j++) {  
                topValue += grid[i][j];  
            }  
        }  
        int bottomValue = totalValue - topValue;  
        minDiff = Math.min(minDiff, Math.abs(topValue - bottomValue));  
    }  
  
    // 枚举纵向分割线  
    for (int col = 0; col < m - 1; col++) { // 分割线在第 col 列右侧  
        int leftValue = 0;  
        for (int i = 0; i < n; i++) {  
            for (int j = 0; j <= col; j++) {  
                leftValue += grid[i][j];  
            }  
        }  
        int rightValue = totalValue - leftValue;  
        minDiff = Math.min(minDiff, Math.abs(leftValue - rightValue));  
    }  
  
    return minDiff;  
}

前缀和的做法,不需要每次都遍历整个矩阵,只需要遍历两个数组即可,时间复杂度会小很多

posted @ 2025-03-27 18:22  Anson_502  阅读(608)  评论(0)    收藏  举报