当前位置: 首页 > news >正文

●day 2 任务以及具体安排:第一章 数组part02

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

解题思路

本题要求找出一个连续子数组 ,其总和≥target ,且长度最小 。由于数组中元素均为正整数,可以使用滑动窗口法 高效求解,时间复杂度为 O(n)。以下是核心思路:

核心思想
  1. 滑动窗口法 :利用两个指针(leftright)表示窗口的左右边界。窗口内的元素和 sum 逐步调整,确保每次操作都能有效缩小搜索范围。
  2. 窗口扩展 :右指针 right 向右移动时,将当前元素值累加到 sum 中。
  3. 窗口收缩 :当 sum ≥ target 时,计算当前窗口长度,并尝试通过移动左指针 left 来缩小窗口,寻找更小的满足条件的子数组。
  4. 记录最小长度 :每次窗口满足条件时,更新最小长度 result,初始设为 Integer.MAX_VALUE,若最终未更新则返回 0。
代码
class Solution {public int minSubArrayLen(int target, int[] nums) {// 初始化左指针、当前窗口和、最小长度int left = 0;int sum = 0;int result = Integer.MAX_VALUE;// 右指针从 0 开始遍历数组for (int right = 0; right < nums.length; right++) {// 将当前元素值累加到窗口和中sum += nums[right];// 当窗口和满足 ≥ target 时,尝试缩小窗口以寻找更小的长度while (sum >= target) {// 更新当前窗口的最小长度result = Math.min(result, right - left + 1);// 缩小窗口:减去左指针的元素值,并右移左指针sum -= nums[left];left++;}}// 如果 result 未被更新,说明无满足条件的子数组,返回 0return result == Integer.MAX_VALUE ? 0 : result;}
}

第二题:59. 螺旋矩阵 II - 力扣(LeetCode)

解题思路

本题要求生成一个 n x n 的螺旋矩阵,其中的元素从 1 按顺时针螺旋顺序填充。该问题的核心在于模拟螺旋填充的过程 ,通过分层遍历的方式逐圈填充矩阵的四边,并正确处理边界条件。


核心思想
  1. 分层填充 :将矩阵分为若干圈(层),每圈处理四条边(上、右、下、左),直到填充完整个矩阵。
  2. 起始点控制 :每圈的起始点为 (startX, startY),初始为 (0, 0),每完成一圈后更新起始点。
  3. 边界控制 :使用 offset 来控制每圈的边界范围,避免覆盖已填充的区域。
  4. 奇数中心点处理 :当 n 为奇数时,最后一圈会剩下一个中心点需要单独填充。
  5. 填充顺序的记忆口诀

        上行左到右,列增行不动;  
        右边上到下,行增列不动;  
        下行右到左,列减行不动;  
        左边下到上,行减列不动。  
        填完一圈,起点进,偏移增;  
        奇数中心点,单独补。  

代码
class Solution {public int[][] generateMatrix(int n) {// 初始化 n x n 的矩阵int[][] nums = new int[n][n];// 定义每一圈的起始位置 (startX, startY)int startX = 0, startY = 0;// 定义循环次数、偏移量、计数器int loop = 1, offset = 1, count = 1;// 每圈循环填充四条边,直到填满所有圈while (loop <= n / 2) {// 1. 填充上边:从左到右(左闭右开)for (int j = startY; j < n - offset; j++) {nums[startX][j] = count++;}// 2. 填充右边:从上到下(上闭下开)for (int i = startX; i < n - offset; i++) {nums[i][n - offset] = count++;}// 3. 填充下边:从右到左(右闭左开)for (int j = n - offset; j > startY; j--) {nums[n - offset][j] = count++;}// 4. 填充左边:从下到上(下闭上开)for (int i = n - offset; i > startX; i--) {nums[i][startY] = count++;}// 进入下一圈loop++;offset++;startX++;startY++;}// 如果 n 为奇数,填充中心点if (n % 2 == 1) {nums[startX][startY] = count;}return nums;}
}
细节解释

 

循环条件 loop <= n / 2 的核心目的是控制螺旋矩阵的填充层数 。具体原因如下:


1. 分层填充的逻辑

螺旋矩阵的填充是按圈进行的,每一圈对应矩阵的一层。例如:

  • n = 3(奇数)时,需要填充 1 层外圈 (覆盖 8 个元素),最后中心点单独填充。
  • n = 4(偶数)时,需要填充 2 层外圈 (覆盖所有 16 个元素),无需单独处理中心点。

因此,总的循环次数(即圈数)为 n / 2向下取整 (即 n // 2)。在代码中,loop 变量表示当前填充的圈数,循环条件 loop <= n / 2 确保所有圈都被正确填充


2. 数学推导:为什么是 n / 2
  • 偶数情况 :若 n = 4,则总共有 4 / 2 = 2 圈。每圈填充后,矩阵的边界会向内收缩 1 层(通过 offset 控制),最终刚好填满整个矩阵。

通过 loop <= n / 2,可以统一处理奇偶情况:

  • n 为偶数时,循环结束后矩阵完全填充。

3. 代码中的实现细节
  • offset 的作用 :每完成一圈,offset 增加 1,用于缩小下一圈的填充范围。例如:
    • 第一圈的上边填充范围是 [startY, n - offset)offset 初始为 1,因此范围是 [0, n - 1)
    • 第二圈的上边范围变为 [1, n - 2),以此类推。

4. 示例验证
示例 1:n = 3(奇数)
  • 循环条件 loop <= 1n / 2 = 1.5,向下取整为 1)。
  • 循环执行 1 次,填充外圈(8 个元素)。
  • 最后通过 if (n % 2 == 1) 填充中心点 5
示例 2:n = 4(偶数)
  • 循环条件 loop <= 2n / 2 = 2)。
  • 循环执行 2 次,填充两圈,完全覆盖所有元素,无需额外处理。

总结

循环条件 loop <= n / 2 的本质是通过数学规律控制螺旋矩阵的填充层数

  • 偶数情况 :恰好填满所有层。
  • 奇数情况 :填满外层后,剩余中心点单独处理。 这种设计既保证了代码的简洁性,又避免了重复填充或遗漏元素的问题

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

解题思路

本题要求对一个整数数组进行多次区间求和查询,每次查询给出左右边界 ab(满足 b >= a),计算数组在区间 [a, b] 内所有元素的和。直接暴力计算每次查询的和会导致时间复杂度为 O(n×q)(q 为查询次数),在数据量较大时容易超时。因此,采用 前缀和 (Prefix Sum)算法优化查询效率。

前缀和原理

前缀和数组 prefix 的第 iprefix[i] 表示原数组从第一个元素到第 i 个元素的累加和(索引从0开始)。通过前缀和数组,任意区间 [a, b] 的和可以由 prefix[b] - prefix[a-1] 快速计算得到(若 a == 0 则直接取 prefix[b]

算法步骤
  1. 预处理前缀和数组

    • 输入数组后,构建前缀和数组 prefix,其中 prefix[i] = prefix[i-1] + array[i]
    • 时间复杂度为 O(n)。
  2. 处理查询

    • 对每个查询 (a, b),根据公式 sum = prefix[b] - (a > 0 ? prefix[a-1] : 0) 计算区间和。
    • 每次查询时间复杂度为 O(1),总体复杂度为 O(q)。
复杂度分析
  • 时间复杂度 :预处理 O(n),查询总时间 O(q),适用于大规模数据。
  • 空间复杂度 :需要额外的前缀和数组,占用 O(n) 空间。
代码
import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner scanner = new Scanner(System.in);// 读取数组长度 nint n = scanner.nextInt();int[] array = new int[n];  // 存储原始数组int[] prefix = new int[n]; // 存储前缀和数组// 读取数组元素for (int i = 0; i < n; i++) {array[i] = scanner.nextInt();}// 构建前缀和数组// 前缀和数组的第一个元素等于原始数组的第一个元素prefix[0] = array[0];// 从第二个元素开始,每个前缀和等于前一个前缀和加上当前元素for (int i = 1; i < n; i++) {prefix[i] = prefix[i - 1] + array[i];}// 处理查询while (scanner.hasNextInt()) {int a = scanner.nextInt(); // 区间左边界int b = scanner.nextInt(); // 区间右边界// 计算区间和int sum;if (a == 0) {sum = prefix[b]; // 当 a=0 时,直接取 prefix[b]} else {sum = prefix[b] - prefix[a - 1]; // 否则用 prefix[b] - prefix[a-1]}System.out.println(sum); // 输出结果}scanner.close(); // 关闭输入流}
}

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

 

解题思路(基于前缀和思想)
核心思想:利用前缀和预处理,快速枚举所有合法划分方式并计算差值
  • 前缀和预处理 :将每行和每列的总和视为一维数组,构建其前缀和序列,从而在划分时快速获取任意前缀区间的总和。
  • 横向划分 :将每行的总和构成一维数组 horizontal,其前缀和 prefix_row[i] 表示前 i+1 行的总和。在任意划分位置 i0 ≤ i < n-1),前缀和 prefix_row[i] 对应前 i+1 行的总和,剩余部分为 sum - prefix_row[i],差值为 |sum - 2 * prefix_row[i]|
  • 纵向划分 :同理,将每列的总和构成一维数组 vertical,其前缀和 prefix_col[j] 表示前 j+1 列的总和。在任意划分位置 j0 ≤ j < m-1),前缀和 prefix_col[j] 对应前 j+1 列的总和,差值为 |sum - 2 * prefix_col[j]|
算法步骤
  1. 读取输入并计算总和

    • 遍历网格 grid[n][m],累加所有元素得到总和 sum
  2. 构建前缀和序列

    • 横向前缀和 :通过累加 horizontal[i] 构建前缀和序列。例如,prefix_row[i] = prefix_row[i-1] + horizontal[i]
  3. 枚举划分并计算差值

    • 横向划分 :遍历 horizontal 数组前 n-1 项,累加得到前缀和 horizontalCut,计算差值 |sum - 2 * horizontalCut|
    • 纵向划分 :遍历 vertical 数组前 m-1 项,累加得到前缀和 verticalCut,计算差值 |sum - 2 * verticalCut|
前缀和的作用与优势
数学推导(差值计算)

设某次划分的前缀和为 cut,则两个子区域的总价值分别为 cutsum - cut,差值为:

∣cut−(sum−cut)∣=∣sum−2×cut∣

此公式将差值计算简化为单次减法和取绝对值操作,无需显式计算两部分和的差值

代码
import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner scanner = new Scanner(System.in);int n = scanner.nextInt(); // 行数int m = scanner.nextInt(); // 列数int sum = 0; // 整个网格的总和int[][] grid = new int[n][m]; // 存储网格数据int[] horizontal = new int[n]; // 每行的总和int[] vertical = new int[m];   // 每列的总和// 读取网格数据并计算总和for (int i = 0; i < n; i++) {for (int j = 0; j < m; j++) {grid[i][j] = scanner.nextInt();sum += grid[i][j];}}// 计算每行的总和(前缀和预处理)for (int i = 0; i < n; i++) {for (int j = 0; j < m; j++) {horizontal[i] += grid[i][j]; // horizontal[i] 表示第 i 行的总和}}// 计算每列的总和(前缀和预处理)for (int j = 0; j < m; j++) {for (int i = 0; i < n; i++) {vertical[j] += grid[i][j]; // vertical[j] 表示第 j 列的总和}}int result = Integer.MAX_VALUE; // 初始化最小差值为最大值// 枚举所有合法的横向划分方式(前缀和应用)int horizontalCut = 0; // 隐式前缀和变量,逐步累加每行的和for (int i = 0; i < n - 1; i++) { // 只遍历 n-1 个合法划分位置horizontalCut += horizontal[i]; // 累加前 i+1 行的总和(前缀和)int diff = Math.abs(sum - 2 * horizontalCut); // 计算差值result = Math.min(result, diff); // 更新最小差值}// 枚举所有合法的纵向划分方式(前缀和应用)int verticalCut = 0; // 隐式前缀和变量,逐步累加每列的和for (int j = 0; j < m - 1; j++) { // 只遍历 m-1 个合法划分位置verticalCut += vertical[j]; // 累加前 j+1 列的总和(前缀和)int diff = Math.abs(sum - 2 * verticalCut); // 计算差值result = Math.min(result, diff); // 更新最小差值}System.out.println(result); // 输出最小差值scanner.close();}
}

相关文章:

  • LM393红外避障电路Multisim仿真
  • Linux进程间通信----简易进程池实现
  • Leetcode 3567. Minimum Absolute Difference in Sliding Submatrix
  • 设备驱动与文件系统:01 I/O与显示器
  • java swing 晃动鼠标改变背景颜色
  • Windows如何定制键盘按键
  • npm install命令都做了哪些事情
  • 基于千帆大模型的AI体检报告解读系统实战:使用OSS与PDFBox实现PDF内容识别
  • UE5.4.4+Rider2024.3.7开发环境配置
  • 设计模式——享元设计模式(结构型)
  • Tomcat 线程模型详解性能调优
  • 如何安装ojdbc6-12.1.0.1与je-5.0.58的mvn构建依赖jar包?
  • 解决Ubuntu20.04上Qt串口通信 QSerialPort 打开失败的问题
  • JMeter 直连数据库
  • Selenium的底层原理
  • 724.寻找数组的中心下标前缀和
  • 《Python基础》第2期:环境搭建
  • 某航后缀混淆逆向与顶像风控分析
  • 循环流化床锅炉关键技术设计与优化路径
  • 【第四十七周】HippoRAG 2 复现与分析(一):环境部署与代码分析
  • saas建站是什么意思/百度大数据预测平台
  • 乐清网站制作哪家好/上海网络推广优化公司
  • 做网站的时候说需求的专业术语/中央广播电视总台
  • 自己做的网站怎么发布win7/培训网站建设
  • 制作广告/宁波谷歌seo推广公司
  • 西安企业排名/官方进一步优化