代码随想录算法训练营第二天 |209.长度最小子数组; 59.螺旋矩阵|| ; 前缀和

今日任务

209.长度最小子数组; 59.螺旋矩阵|| 58.区间和 44.开发商购买土地

209.长度最小的子数组

题目建议: 本题关键在于理解滑动窗口,这个滑动窗口看文字讲解 还挺难理解的,建议大家先看视频讲解。 拓展题目可以先不做。

题目链接:. - 力扣(LeetCode)

文章讲解:代码随想录

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

    //连续: 滑动窗口
    public static int minSubArrayLen(int target, int[] nums) {
        if(nums == null || nums.length == 0){
            return 0;
        }
        int l=0,r=0;
        int minLen = Integer.MAX_VALUE;
        //计算前缀和
        int[] sumArr = new int[nums.length+1];
        sumArr[0] = nums[0];
        for(int i=1;i< nums.length;i++){
            sumArr[i] = nums[i] + sumArr[i-1];
        }
        //计算累加值最小
        while(l<=r && r <= nums.length-1){
            //当前滑动窗口内的和
            int curSum = l == 0 ? sumArr[r] : sumArr[r] - sumArr[l-1];
            //不满足条件,则窗口右端右移扩大范围
            if(curSum < target){
                r++;
                continue;
            }
            //满足条件,则窗口左端右移尝试缩小范围
            if(curSum >= target){
                minLen = Math.min(r-l+1,minLen);
                l++;
                continue;
            }
        }
        return minLen == Integer.MAX_VALUE ? 0:minLen;
    }
核心点:

        看到连续和的时候,优先想到的就是滑动窗口与前缀和,滑动窗口的题型,关键点需要理解什么情况下需要扩大/缩小窗口范围

59.螺旋矩阵II

题目建议: 本题关键还是在转圈的逻辑,在二分搜索中提到的区间定义,在这里又用上了。

题目链接:. - 力扣(LeetCode)

文章讲解:代码随想录

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

class Solution {
    public int[][] generateMatrix(int n) {
        int[][] res = new int[n][n];
        //共几圈
        int totalRound = n%2 + n/2;
        dfs(res,1,totalRound,1,n);
        return res;
    }

    /**
     * 如图二维数组对应的坐标为(x,y)
     * +-----------------> x
     * | (0,0) (0,1) (0,2)
     * | (1,0) (1,1) (1,2)
     * ↓ (2,0) (2,1) (2,2)
     * y
     * @param res
     * @param start 起始值
     * @param width 正方形边长,初始为n,每缩小一圈边长减2
     * @param round 第几圈: 最外圈认为是第一圈
     */
    public static void dfs(int[][] res,int round,int totalRound,int start,int width){
        if(round > totalRound){
            return;
        }
        int x = round-1;
        int y = round-1;
        int idxEnd = round+width-2;
        边长为1时,则需要设置中心值
        if(width == 1){
            res[x][y] = start;
            return;
        }
        //顶
        for (;y < idxEnd; y++) {
            res[round-1][y] = start++;
        }
        //右
        for (;x < idxEnd; x++) {
            res[x][idxEnd] = start++;
        }
        //底
        for (;y > round-1; y--) {
            res[idxEnd][y] = start++;
        }
        //左
        for (; x > round-1; x--) {
            res[x][y] = start++;
        }
        //width 边长每次都减2 --> 左边减一右边减一
        dfs(res,round+1,totalRound,start,width-2);
    }
}

核心点

        解题的关键在于几个关键点:

  1. 明确循环不变量原则或者是找到循环体
  2. 理解每层正方形的变成差值为2
  3. 拆分成 顶边(x不变y递增),右边(y不变x递增),底边(x不变y递减),左边(y不变x递减)
  4. 按着上面的原则将多层正方形的填空题比变成了单层正方形的填空题

58.区间和

前缀和是一种思维巧妙很实用 而且 很有容易理解的一种算法思想,大家可以体会一下

文章讲解:58. 区间和 | 代码随想录
题目描述

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

输入描述

第一行输入为整数数组 Array 的长度 n,接下来 n 行,每行一个整数,表示数组的元素。随后的输入为需要计算总和的区间下标:a,b (b > = a),直至文件结束。

输出描述

输出每个指定区间内元素的总和。

输入示例
5
1
2
3
4
5
0 1
1 3
输出示例
3
9
提示信息

数据范围:
0 < n <= 100000

import java.util.Scanner;

public class Main {
    public static void main (String[] args) {
        Scanner sc = new Scanner(System.in);
        //arr长度 len
        int len = sc.nextInt();
        int[] arr = new int[len];
        //补充数组元素
        for(int i=0;i<len;i++){
            arr[i] = sc.nextInt();
        }

        int[] sum = new int[len];
        sum[0] = arr[0];
        //计算前缀和
        for(int i=1;i<len;i++){
            sum[i] = sum[i-1]+arr[i];
        }

        //输入范围
        int left,right;
        while(sc.hasNextInt()){
            left = sc.nextInt();
            right = sc.nextInt();
            int res = (left == 0) ? sum[right] : sum[right]- sum[left-1];
            System.out.println(res);
        }
    }
}
补充(Java ACM输入输出):
常用类
import java.util.Scanner;
常用API
//构造方法
//从文件中读取数据
//Scanner sc = new Scanner(File source);
//从输入流中读取数据
//Scanner sc = new Scanner(InputStream source);
//从字符串中读取数据
//Scanner sc = new Scanner(String source);
//直接接受系统控制台的输入
Scanner sc = new Scanner(System.in);

//数据读取
//读取字符串,遇到空格或换行符停止
sc.next();
//读取整数
sc.nextInt();
//读取双精度浮点数
sc.nextDouble();
//读取一行字符串,包括换行符
sc.nextLine();

//数据判断
//判断是否有下一个字符串
sc.hasNext();
//判断是否有下一个整数
sc.hasNextInt();
//判断是否有下一个双精度浮点数
sc.hasNextDouble();
//方法来判断是否还有下一行输入
sc.hasNextLine();

44.开发商购买土地

44. 开发商购买土地 | 代码随想录

【题目描述】

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

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

然而,由于城市规划的限制,只允许将区域按横向或纵向划分成两个子区域,而且每个子区域都必须包含一个或多个区块。

为了确保公平竞争,你需要找到一种分配方式,使得 A 公司和 B 公司各自的子区域内的土地总价值之差最小。

注意:区块不可再分。

【输入描述】

第一行输入两个正整数,代表 n 和 m。

接下来的 n 行,每行输出 m 个正整数。

输出描述

请输出一个整数,代表两个子区域内土地总价值之间的最小差距。

【输入示例】

3 3 1 2 3 2 1 3 1 2 3

【输出示例】

0

【提示信息】

如果将区域按照如下方式划分:

1 2 | 3 2 1 | 3 1 2 | 3

两个子区域内土地总价值之间的最小差距可以达到 0。

【数据范围】:

  • 1 <= n, m <= 100;
  • n 和 m 不同时为 1。
import java.util.Scanner;

public class Main {
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        //System.out.println("输入n");
        int n = sc.nextInt();
        //System.out.println("输入m");
        int m = sc.nextInt();
        int[][] arr = new int[n][m];
        for (int i = 0; i < n; i++) {
            //System.out.println("输入第"+(i+1)+"行");
            for (int j = 0; j < m; j++) {
                arr[i][j] = sc.nextInt();
            }
        }
        //二维数组所有元素和
        int total = 0;
        
        //计算前缀和
        int[][] sum = new int[n][m];
        for (int i = 0; i < n; i++) {
            sum[i][0] = arr[i][0];
            total += m == 1 ? sum[i][0]:0; 
        }
        
        for (int i = 0; i < n; i++) {
            for (int j = 1; j < m; j++) {
                sum[i][j] = sum[i][j-1] + arr[i][j];
                total += ((j == (m-1)) ? sum[i][j] : 0);
            }
        }
        //计算最终输出结果
        int min = Integer.MAX_VALUE;
        //按y轴切割
        for (int i = 0; i < n-1; i++){
            int top = sumY(sum,i,m);
            int down = total-top;
            min = Math.min(min,Math.abs(top-down));
        }
        //按x轴切割
        for (int i = 0; i < m-1; i++){
            int left = sumX(sum,i,n);
            int right = total-left;
            min = Math.min(min,Math.abs(left-right));
        }
        System.out.println(min);
    }



    /**
     * 横向统计 -- 累加每个数组的最后一个元素
     * @return
     */
    public static int sumY(int[][] sum,int y,int m){
        int res = 0;
        for (int i = 0; i <= y; i++) {
            res += sum[i][m-1];
        }
        return res;
    }

    /**
     * 纵向统计
     * @return
     */
    public static int sumX(int[][] sum,int x,int n){
        int res = 0;
        for (int i = 0; i < n; i++) {
            res += sum[i][x];
        }
        return res;
    }
}

总结

       数组通常的考察方向有三点:

  1. 二分法
  2. 双指针(快慢指针/对向指针/滑动窗口)
  3. 关键是需要搞清楚什么节点需要移动哪个指针
  4. 循环不变量原则(最小循环体)
  5. 前缀和
  • 24
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
第二十二天的算法训练营主要涵盖了Leetcode题目中的三道题目,分别是Leetcode 28 "Find the Index of the First Occurrence in a String",Leetcode 977 "有序数组的平方",和Leetcode 209 "长度最小的子数组"。 首先是Leetcode 28题,题目要求在给定的字符串中找到第一个出现的字符的索引。思路是使用双指针来遍历字符串,一个指向字符串的开头,另一个指向字符串的结尾。通过比较两个指针所指向的字符是否相等来判断是否找到了第一个出现的字符。具体实现的代码如下: ```python def findIndex(self, s: str) -> int: left = 0 right = len(s) - 1 while left <= right: if s[left == s[right]: return left left += 1 right -= 1 return -1 ``` 接下来是Leetcode 977题,题目要求对给定的有序数组中的元素进行平方,并按照非递减的顺序返回结果。这里由于数组已经是有序的,所以可以使用双指针的方法来解决问题。一个指针指向数组的开头,另一个指针指向数组的末尾。通过比较两个指针所指向的元素的绝对值的大小来确定哪个元素的平方应该放在结果数组的末尾。具体实现的代码如下: ```python def sortedSquares(self, nums: List[int]) -> List[int]: left = 0 right = len(nums) - 1 ans = [] while left <= right: if abs(nums[left]) >= abs(nums[right]): ans.append(nums[left ** 2) left += 1 else: ans.append(nums[right ** 2) right -= 1 return ans[::-1] ``` 最后是Leetcode 209题,题目要求在给定的数组中找到长度最小的子数组,使得子数组的和大于等于给定的目标值。这里可以使用滑动窗口的方法来解决问题。使用两个指针来表示滑动窗口的左边界和右边界,通过移动指针来调整滑动窗口的大小,使得滑动窗口中的元素的和满足题目要求。具体实现的代码如下: ```python def minSubArrayLen(self, target: int, nums: List[int]) -> int: left = 0 right = 0 ans = float('inf') total = 0 while right < len(nums): total += nums[right] while total >= target: ans = min(ans, right - left + 1) total -= nums[left] left += 1 right += 1 return ans if ans != float('inf') else 0 ``` 以上就是第二十二天的算法训练营的内容。通过这些题目的练习,可以提升对双指针和滑动窗口等算法的理解和应用能力。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值