有序数组的平方
题目描述
给你一个按 非递减顺序 排序的整数数组 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]
解题思路
暴力排序
核心思路:依次遍历数组元素,将每个元素都进行平方,然后进行排序即可。
时间复杂度:O(n + nlogn)
双指针法
核心思路:由于数组是有序的,但是数组元素包含负数,平方后可能会改变原有的顺序,因此可以采用双指针法进行问题解决。
具体步骤:
- 初始化数组用于保存新排列后的平方值
- 由于数组是有序的,因此在开方后,最大的值只会出现在左右两侧,因此可以将第一个指针指向最左侧元素,第二个指针指向最右侧元素,从左右两侧向中间不断逼近
- 比较左右指针指向元素平方后的值,由于题目提到了从小到大排序,因此需要将较大的值放在数组最后的位置,依次执行,最后返回平方后的数组即可。
时间复杂度:O(n)
代码实现
测试地址:https://leetcode.cn/problems/squares-of-a-sorted-array/
暴力实现:
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
// 遍历输入数组的每个元素
for (int i = 0; i < nums.size(); i++) {
// 将每个元素替换为其平方
nums[i] *= nums[i];
}
// 使用标准库中的sort函数对数组进行排序
sort(nums.begin(), nums.end());
// 返回修改后的数组
return nums;
}
};
双指针实现:
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
// 初始化结果数组,大小与输入数组相同,初始值为0
vector<int> result(nums.size(), 0);
// k用于从结果数组的末尾开始填充
int k = nums.size() - 1;
// 使用双指针技术,i从数组开始处迭代,j从数组末尾迭代
for (int i = 0, j = nums.size() - 1; i <= j;) {
// 比较两个指针所指元素的平方值,将较大的平方值放到结果数组的末尾
if ((nums[i] * nums[i]) > (nums[j] * nums[j])) {
// 如果i指向的元素的平方大于j指向的元素的平方,
// 则将i指向的元素的平方赋值给结果数组的当前末尾位置,
// 然后移动i指针向右一位,k指针向左一位
result[k--] = nums[i] * nums[i];
i++;
} else {
// 如果j指向的元素的平方大于等于i指向的元素的平方,
// 则将j指向的元素的平方赋值给结果数组的当前末尾位置,
// 然后移动j指针向左一位,k指针向左一位
result[k--] = nums[j] * nums[j];
j--;
}
}
// 返回填充完成的结果数组
return result;
}
};
长度最小的子字符串
题目描述
给定一个含有 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
解题思路
暴力
总体思路:
通过暴力方法寻找一个最短的子数组,使得子数组中的元素总和至少为 target
。它通过两层循环,外层循环确定子数组的起始位置,内层循环扩展子数组的结束位置,直到找到一个满足条件的子数组。一旦找到,它会记录下当前找到的满足条件的最短子数组的长度,并尝试找到更短的一个,直到遍历完整个数组。
时空复杂度:
- 时间复杂度:O(n^2)
- 空间复杂度:O(1)
滑动窗口
滑动窗口介绍:通过不断调节子序列起始位置和终止位置,从而得到我们想要的结果。
总体思路:
实现滑动窗口,满足窗口内包含的子序列和>=给定的条件值s
窗口起始位置移动:如果当前窗口已经大余或者等于s,将窗口向右侧移动,即缩小窗口范围
窗口终止位置移动:依次遍历数组的元素并进行累加操作,直至找到最先满足子序列和>=s的下标位置,即扩大窗口范围。
代码实现
测试地址:https://leetcode.cn/problems/minimum-size-subarray-sum/description/
暴力:
class Solution {
public:
// 定义一个函数,接收目标值 target 和一个整数数组 nums 作为参数
int minSubArrayLen(int target, vector<int>& nums) {
// 初始化 sum 为 0,用来在每次内层循环开始时重置子数组的和
// result 用来保存最小子数组的长度,初始设置为 INT_MAX,表示最大可能的整数值
int sum = 0, result = INT_MAX;
// subLength 用来暂存当前找到的满足条件的子数组的长度
int subLength = 0;
// 外层循环,i 标记当前子数组的起始位置
for (int i = 0; i < nums.size(); i++) {
// 每次开始新的子数组时,重置 sum 为 0
sum = 0;
// 内层循环,j 标记当前子数组的结束位置
for (int j = i; j < nums.size(); j++) {
// 累加当前元素到 sum 中,扩展当前子数组的范围
sum += nums[j];
// 当当前子数组的和大于等于 target 时
if (sum >= target) {
// 计算当前子数组的长度
subLength = j - i + 1;
// 更新 result 为当前子数组长度和之前 result 中的较小值
result = std::min(result, subLength);
// 找到符合条件的子数组后跳出内层循环
break;
}
}
}
// 如果没有找到满足条件的子数组,返回 0;否则,返回找到的最短子数组的长度
return result == INT_MAX ? 0 : result;
}
};
滑动窗口:
class Solution {
public:
// 定义一个函数,接收目标值 target 和一个整数数组 nums 作为参数
int minSubArrayLen(int target, vector<int>& nums) {
// 初始化 sum 为 0,用来存储子数组的和
// result 用来保存最小子数组的长度,初始设置为 INT_MAX,表示最大可能的整数值
// i 用来标记当前考虑的子数组的起始位置
int sum = 0, result = INT_MAX, i = 0;
// subLength 用来暂存当前找到的满足条件的子数组的长度
int subLength = 0;
// 遍历 nums 数组,j 用来标记当前考虑的子数组的结束位置
for (int j = 0; j < nums.size(); j++) {
// 累加当前元素到 sum 中,扩展当前子数组的范围
sum += nums[j];
// 当当前子数组的和大于等于 target 时,尝试缩小子数组的范围来寻找更短的满足条件的子数组
while (sum >= target) {
// 计算当前子数组的长度
subLength = j - i + 1;
// 更新 result 为当前子数组长度和之前 result 中的较小值
result = min(result, subLength);
// 从子数组的和中减去当前考虑的子数组的开始元素,并移动起始位置指针
sum -= nums[i++];
}
}
// 如果没有找到满足条件的子数组,返回 0;否则,返回找到的最短子数组的长度
return result == INT_MAX ? 0 : result;
}
};
螺旋矩阵
题目描述
给定一个正整数 n,生成一个包含 1 到 n^2 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵。
示例:
输入: 3 输出: [ [ 1, 2, 3 ], [ 8, 9, 4 ], [ 7, 6, 5 ] ]
解题思路
原则:循环不变量原则,即每个区间处理边界的方式都是一致的,本题中可以按照左闭右开的原则
总体思路:模拟顺时针画矩阵的过程
- 填充上行从左到右
- 模拟右列从上到下
- 模拟下行从右到左
- 模拟左列从下到上
实现细节
- 如果n是偶数的话,循环的圈数为 n / 2
- 如果n是奇数的话,可以先进行偶数的循环,最后单独考虑最后一个中点位置
- 因为每圈都会固定缩小区间范围,因此可以引入一个参数offset方便判断边界
时空复杂度分析
- 时间复杂度 O(n^2): 模拟遍历二维矩阵的时间
- 空间复杂度 O(1)
代码实现
测试地址:https://leetcode.cn/problems/spiral-matrix-ii/description/
class Solution {
public:
// 函数生成一个n x n的螺旋矩阵
vector<vector<int>> generateMatrix(int n) {
// 初始化一个n x n的矩阵,初始值为0
vector<vector<int>> res(n, vector<int>(n, 0));
// 定义起始坐标
int startx = 0, starty = 0;
// 定义偏移量,用于控制每一层填充的边界
int offset = 1;
// 定义填充的起始数字
int count = 1;
// 定义循环的圈数,每一圈对应矩阵中的一层螺旋
int loop = n / 2;
// 如果n为奇数,矩阵中心点的坐标
int mid = n / 2;
// 定义循环中使用的索引变量
int i, j;
// 循环填充每一层螺旋
while (loop--) {
// 从左上角开始
i = startx, j = starty;
// 从左到右填充上行
for (j; j < n - offset; j++) {
res[i][j] = count++;
}
// 从上到下填充右列
for (i; i < n - offset; i++) {
res[i][j] = count++;
}
// 从右到左填充下行
for (; j > starty; j--) {
res[i][j] = count++;
}
// 从下到上填充左列
for (; i > startx; i--) {
res[i][j] = count++;
}
// 移动到下一层螺旋的起始位置
startx++, starty++;
// 更新偏移量,准备填充下一层螺旋
offset += 1;
}
// 如果n为奇数,填充矩阵的中心点
if (n % 2) {
res[mid][mid] = count;
}
// 返回生成的螺旋矩阵
return res;
}
};