977.有序数组的平方
题目
给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
示例 1:
-
输入:nums = [-4,-1,0,3,10]
-
输出:[0,1,9,16,100]
-
解释:平方后,数组变为 [16,1,0,9,100],排序后,数组变为 [0,1,9,16,100]
示例 2:
-
输入:nums = [-7,-3,2,3,11]
-
输出:[4,9,9,49,121]
思路
如果数组没有包含负数,那么递增的数组[0,3,10]
,平方后,数组仍然还是递增 [0,9,100]
但是这里包含了负数,所以平方后,最大值出现在最左边或者最右边,不可能出现在中间;
由此可以使用双指针算法。left=0;right=nums.length-1;比较平方后的值,放在新数组的最后位置。然后向中间移动指针。
代码
class Solution {
public int[] sortedSquares(int[] nums) {
int left = 0;
int right = nums.length - 1; // 使用闭区间,left==right有意义
int[] newNums = new int[nums.length];
int lastIndex = newNums.length - 1;
while (left <= right) {
int leftSq = nums[left] * nums[left];
int rightSq = nums[right] * nums[right];
// 比较平方最大值
if (leftSq > rightSq) {
newNums[lastIndex] = leftSq;
lastIndex--; // 新数组最大值向后走
left++; // 左指针中间走
} else {
newNums[lastIndex] = rightSq;
lastIndex--;
right--;
}
}
return newNums;
}
}
209.长度最小的子数组
题目
给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组
,并返回其长度。如果不存在符合条件的子数组,返回 0。
示例:
-
输入:s = 7, nums = [2,3,1,2,4,3]
-
输出:2
-
解释:子数组 [4,3] 是该条件下的长度最小的子数组。
提示:
-
1 <= target <= 10^9
-
1 <= nums.length <= 10^5
-
1 <= nums[i] <= 10^5
思路
我想到的思路是用滑动窗口。
详细解释:
-
初始化变量:
-
result
用于存储最短的子数组长度,初始值为Integer.MAX_VALUE
,方便后续比较和更新。 -
sum
用于存储当前窗口内所有元素的和。 -
i
是滑动窗口的起始位置。 -
subLength
用于存储当前窗口的长度。
-
-
遍历数组:
-
使用
for
循环遍历数组,其中j
表示滑动窗口的结束位置。
-
-
更新窗口和:
-
每次循环将
nums[j]
加到sum
中,更新窗口内的和。
-
-
缩小窗口:
-
当
sum
大于或等于target
时,进入while
循环尝试缩小窗口。 -
计算当前窗口长度,并更新
result
为更小的值。 -
从
sum
中减去nums[i]
,并将起始位置i
右移,缩小窗口。
-
-
返回结果:
-
如果
result
仍为Integer.MAX_VALUE
,说明没有找到符合条件的子数组,返回0。 -
否则,返回
result
,即最短的子数组长度。
-
这个版本的代码逻辑清晰,注释详细,便于理解和维护。
代码
我觉得好理解的版本
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int result = Integer.MAX_VALUE; // 初始化结果为最大值,以便后续比较
int sum = 0; // 初始化滑动窗口内元素之和为0
int i = 0; // 初始化滑动窗口的起始位置
int subLength = 0; // 初始化滑动窗口的长度
// 遍历数组,j 表示滑动窗口的结束位置
for (int j = 0; j < nums.length; j++) {
// 增加窗口的右边界,并更新窗口内元素之和
sum += nums[j];
// 当窗口内元素之和大于等于目标值时,开始尝试缩小窗口
while (sum >= target) {
// 计算当前窗口长度
subLength = j - i + 1;
// 更新最短长度
result = Math.min(result, subLength);
// 缩小窗口,移除左边界的元素,并将起始位置右移
sum -= nums[i];
i++;
}
}
// 如果结果未更新,则返回0;否则返回最短长度
return result == Integer.MAX_VALUE ? 0 : result;
}
}
版本1
class Solution {
public int minSubArrayLen(int target, int[] nums) {
// 初始化窗口双指针:left 指向窗口左边界,right 指向窗口右边界
int left = 0;
int right = 0;
// 初始化返回结果,将其设置为一个较大的值以便后续比较
int res = Integer.MAX_VALUE;
// 初始化子数组的和为0
int sum = 0;
// 开始滑动窗口,当右边界未越界时
while (right < nums.length) {
// 先将当前右边界的值加到 sum 中
sum += nums[right];
// 尝试缩小窗口
while (sum >= target) {
// 计算当前窗口长度 ,注right - left + 1是个数学问题
int subLength = right - left + 1;
// 更新结果,选择更小的长度
res = Math.min(res, subLength);
// 移动左边界,同时减去左边界值从 sum 中
sum -= nums[left];
left++;
}
// 移动右边界,继续扩展窗口
right++;
}
// 如果 res 没有更新,说明没有符合条件的子数组,返回0
return res == Integer.MAX_VALUE ? 0 : res;
}
}
59.螺旋矩阵II
题目
给定一个正整数 n,生成一个包含 1 到 n^2 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵。
示例:
输入: 3 输出: [ [ 1, 2, 3 ], [ 8, 9, 4 ], [ 7, 6, 5 ] ]
思路
那么我按照左闭右开的原则,来画一圈,大家看一下:
这里每一种颜色,代表一条边,我们遍历的长度,可以看出每一个拐角处的处理规则,拐角处让给新的一条边来继续画。
代码
class Solution {
public int[][] generateMatrix(int n) {
// 初始化偏移量,用于调整每圈的边界
int offset = 1;
// 初始化起始位置坐标
int startX = 0;
int startY = 0;
// 初始化计数器,从1开始填充矩阵
int count = 1;
// 计算需要迭代的圈数
int loop = n / 2;
// 计算矩阵中间的位置,用于处理奇数维度的情况
int mid = n / 2;
// 定义结果矩阵
int[][] nums = new int[n][n];
// 迭代每一圈
while (loop > 0) {
// 从左到右填充当前圈的上边
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++;
// 增加偏移量
offset++;
// 减少圈数
loop--;
}
// 如果 n 是奇数,单独填充矩阵的中心位置
if (n % 2 != 0) {
nums[mid][mid] = count;
}
// 返回填充完毕的矩阵
return nums;
}
}