From 46a9f57fb251b105739d94576f840d0bdd87ac4b Mon Sep 17 00:00:00 2001 From: zhangsan <646228430@qq.com> Date: Sun, 15 Jun 2025 10:02:13 +0800 Subject: [PATCH] =?UTF-8?q?6.15=20=E4=BA=8C=E5=88=B7hot100=20=E6=99=AE?= =?UTF-8?q?=E9=80=9A=E6=95=B0=E7=BB=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/array/FirstMissingPositive.java | 58 ++++++++++++++ src/main/java/array/ProductExceptSelf.java | 50 +++++++++--- src/main/java/matrix/SetZeroes.java | 78 ++++++++++++++++++- 3 files changed, 173 insertions(+), 13 deletions(-) create mode 100644 src/main/java/array/FirstMissingPositive.java diff --git a/src/main/java/array/FirstMissingPositive.java b/src/main/java/array/FirstMissingPositive.java new file mode 100644 index 0000000..32c5ca4 --- /dev/null +++ b/src/main/java/array/FirstMissingPositive.java @@ -0,0 +1,58 @@ +package array; +/** + * 题目:41. 缺失的第一个正数 (firstMissingPositive) + * 描述:给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数。 + * + * 请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。 + + * + 示例 1: + 输入:nums = [1,2,0] + 输出:3 + 解释:范围 [1,2] 中的数字都在数组中。 + + * 链接:https://leetcode.cn/problems/first-missing-positive/description/ + */ +//不会 +public class FirstMissingPositive { + /** + * 定位阶段 + * 遍历数组,对每个元素 nums[i],如果它满足 1 ≤ nums[i] ≤ n,且没有放在正确的位置上(即 nums[nums[i]−1] ≠ nums[i]),就把它和目标位置上的元素交换。 + * + * 这样做相当于把所有「有可能成为第一个缺失正数」的数放到它们理想的位置上。 + * 每个元素最多交换一次或两次,因此这一阶段仍是 O(n)。 + * 检查阶段 + * 再次遍历数组,找到第一个下标 i,使得 nums[i] ≠ i+1,此时 i+1 就是缺失的最小正数; + * 如果遍历结束都没发现不匹配的,则说明 1…n 都出现过,答案是 n+1。 + * + * 为什么是常数空间? + * 我们只是在原数组上做交换,没有使用额外的数组或哈希表。 + * @param nums + * @return + */ + public int firstMissingPositive(int[] nums) { + int n = nums.length; + // 定位阶段:把每个 1...n 的数放到下标 num-1 + for (int i = 0; i < n; i++) { + // 当 nums[i] 落在 [1, n] 且没有放在正确位置时,交换到正确位置 + while (nums[i] >= 1 && nums[i] <= n && nums[nums[i] - 1] != nums[i]) { + swap(nums, i, nums[i] - 1); + } + } + // 检查阶段:第一个 nums[i] != i+1 的下标即答案 + for (int i = 0; i < n; i++) { + if (nums[i] != i + 1) { + return i + 1; + } + } + // 全都对上了,则缺失的是 n+1 + return n + 1; + } + + // 交换数组中两个元素 + private void swap(int[] arr, int i, int j) { + int tmp = arr[i]; + arr[i] = arr[j]; + arr[j] = tmp; + } +} diff --git a/src/main/java/array/ProductExceptSelf.java b/src/main/java/array/ProductExceptSelf.java index e5289a9..e97f85a 100644 --- a/src/main/java/array/ProductExceptSelf.java +++ b/src/main/java/array/ProductExceptSelf.java @@ -16,24 +16,50 @@ package array; 输出: [0,0,9,0,0] */ //没做出来 +//二刷不会 public class ProductExceptSelf { + /** + * 用一个数组 left 存储当前位置左侧所有元素的乘积,left[0]=1,每往右一步就把前一步的乘积乘上前一步的元素。 + * 用一个数组 right 存储当前位置右侧所有元素的乘积,right[n-1]=1,每往左一步就把后一步的乘积乘上后一步的元素。 + * 最终结果 res[i] = left[i] * right[i],既含括了除自身以外的所有元素。 + * 这样只需两次线性遍历,时间 O(n),不使用除法;若要 O(1) 额外空间,可把前缀积直接存到结果数组,再用一个变量累积后缀积。 + * @param nums + * @return + */ public int[] productExceptSelf(int[] nums) { - int size=nums.length; - if(size==0) - return new int[]{}; - int[] left=new int[size],right=new int[size]; - left[0]=1; - right[size-1]=1; + int size = nums.length; // 数组长度 + if (size == 0) // 如果输入数组为空 + return new int[]{}; // 返回空数组 + + int[] left = new int[size], // left[i] 存 nums[0..i-1] 的乘积 + right = new int[size]; // right[i] 存 nums[i+1..n-1] 的乘积 + + left[0] = 1; // 第一个元素左侧没有元素,乘积为 1 + right[size - 1] = 1; // 最后一个元素右侧没有元素,乘积为 1 + + // 计算所有前缀积 for (int i = 1; i < size; i++) { - left[i]=left[i-1]*nums[i-1]; + // left[i] = left[i-1] * nums[i-1] + // 即当前位置左侧乘积 = 前一个左侧乘积 × 前一个元素 + left[i] = left[i - 1] * nums[i - 1]; } - for (int j = size-2; j >=0 ; j--) { - right[j]=right[j+1]*nums[j+1]; + + // 计算所有后缀积 + for (int j = size - 2; j >= 0; j--) { + // right[j] = right[j+1] * nums[j+1] + // 即当前位置右侧乘积 = 后一个右侧乘积 × 后一个元素 + right[j] = right[j + 1] * nums[j + 1]; } - int[]res=new int[size]; + + int[] res = new int[size]; // 用于存放最终结果 + + // 合并前缀积和后缀积 for (int k = 0; k < size; k++) { - res[k]=left[k]*right[k]; + // 排除自身的所有元素乘积 = left[k] × right[k] + res[k] = left[k] * right[k]; } - return res; + + return res; // 返回结果数组 } + } diff --git a/src/main/java/matrix/SetZeroes.java b/src/main/java/matrix/SetZeroes.java index e1b0e6c..ac7ede3 100644 --- a/src/main/java/matrix/SetZeroes.java +++ b/src/main/java/matrix/SetZeroes.java @@ -15,8 +15,10 @@ import java.util.HashSet; 输入:matrix = [[0,1,2,0],[3,4,5,2],[1,3,1,5]] 输出:[[0,0,0,0],[0,4,5,0],[0,3,1,0]] */ +//二刷不会原地算法 public class SetZeroes { - public void setZeroes(int[][] matrix) { + //非原地算法 + public void setZeroes1(int[][] matrix) { int row=matrix.length; int col=matrix[0].length; HashSetiset=new HashSet<>(); @@ -40,4 +42,78 @@ public class SetZeroes { } } } + + /** + * 预处理第一行和第一列 + * 遍历第一行,看有没有 0,记到布尔变量 firstRowZero + * 遍历第一列,看有没有 0,记到布尔变量 firstColZero + * 这样做是因为我们后面要把其余位置的「标记」都写到第一行/第一列上,不能让原有的第 1 行/1 列信息丢失。 + * + * 用第一行/列做标记 + * 从 (i=1,j=1) 开始遍历矩阵, + * 如果 matrix[i][j] == 0,则将 matrix[i][0] = 0(标记该行要置零)和 matrix[0][j] = 0(标记该列要置零)。 + * + * 根据标记清零 + * 再次遍历 (i=1,j=1) 区域: + * 如果 matrix[i][0] == 0 或 matrix[0][j] == 0,就把 matrix[i][j] 设为 0。 + * + * 处理第 1 行和第 1 列 + * 如果 firstRowZero 为 true,就整行清 0; + * 如果 firstColZero 为 true,就整列清 0。 + * @param matrix + */ + public void setZeroes(int[][] matrix) { + int m = matrix.length; + int n = matrix[0].length; + boolean firstRowZero = false; + boolean firstColZero = false; + + // 1. 预处理第一行 + for (int j = 0; j < n; j++) { + if (matrix[0][j] == 0) { + firstRowZero = true; + break; + } + } + + // 1. 预处理第一列 + for (int i = 0; i < m; i++) { + if (matrix[i][0] == 0) { + firstColZero = true; + break; + } + } + + // 2. 用第一行/列做标记 + for (int i = 1; i < m; i++) { + for (int j = 1; j < n; j++) { + if (matrix[i][j] == 0) { + matrix[i][0] = 0; + matrix[0][j] = 0; + } + } + } + + // 3. 根据标记清零内部区域 + for (int i = 1; i < m; i++) { + for (int j = 1; j < n; j++) { + if (matrix[i][0] == 0 || matrix[0][j] == 0) { + matrix[i][j] = 0; + } + } + } + + // 4. 清零第一行(如果需要) + if (firstRowZero) { + for (int j = 0; j < n; j++) { + matrix[0][j] = 0; + } + } + // 4. 清零第一列(如果需要) + if (firstColZero) { + for (int i = 0; i < m; i++) { + matrix[i][0] = 0; + } + } + } }