文章目录
今日记录
977.有序数组的平方
给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序
直接排序
时间复杂度:O(nlogn) 取决于快排
for循环:O(n) ✖️ 快排:O(nlogn)
因此时间复杂度是 O(n + nlogn), 可以说是O(nlogn)的时间复杂度,但为了和下面双指针法算法时间复杂度有鲜明对比,记为 O(n + nlog n)。
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
for (int i = 0; i < nums.size(); i++) {
nums[i] *= nums[i];
}
sort(nums.begin(), nums.end()); // 快速排序
return nums;
}
};
双指针法
1. 思路
题目中数组同样是有序的,平方后也可以知道最大值一定在数组的两端,此时考虑用双指针法。
i,j 分别表示左右两端,定义一个新数组result,和A数组一样的大小,让k指向result数组终止位置(由于新数组也是按照非递减排列的)。
如果A[ i ] * A[ i ] < A[ j ] * A[ j ] 那么 result [k- -] = A[ j ] * A[ j ]
如果A[ i ] * A[ i ] >= A[ j ] * A[ j ] 那么 result [k- -] = A[ i ] * A[ i ]; 。
2. 代码实现
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
vector<int> result(nums.size(), 0);
int i = 0, j = nums.size() - 1;
int k = nums.size() - 1;
while (i <= j) {
if (nums[i] * nums[i] > nums[j] * nums[j]) {
result[k--] = nums[i] * nums[i];
i++;
} else {
result[k--] = nums[j] * nums[j];
j--;
}
}
return result;
}
};
时间复杂度:O(n)
209.长度最小的子数组
给定一个含有 n 个正整数的数组和一个正整数 target ,找出该数组中满足其和 ≥ target 的长度最小的 连续 子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。
直接求解(双层for循环)
思路为:i表示起始位置,i取不同位置时j直接遍历后续位置求和,一旦满足>=target条件计算此时的长度sublen,与前面所有情况的最小值result进行比较。
时间复杂度:O(n^2)
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int result = INT32_MAX; // 整型变量能够存储的最大正整数
int sum = 0;
int sublen = 0;
for (int i = 0; i < nums.size(); i++) {
sum = 0;
for (int j = i; j < nums.size(); j++) {
sum += nums[j];
if (sum >= target) {
sublen = j - i + 1;
result = result < sublen ? result : sublen;
break;
}
}
}
return result == INT32_MAX ? 0 : result;
}
};
滑动窗口法
1. 思想
所谓滑动窗口,就是不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果。
用 i 表示滑动窗口的起始位置,j 表示滑动窗口的结束位置,用一个for 循环来实现。
2. 难点
-
窗口内是什么?
窗口就是 满足其和 ≥ s 的长度最小的 连续 子数组。 -
如何移动窗口的起始位置?
-
如何移动窗口的结束位置?
首先讨论 for ( i or j ?) —— 是移动滑动窗口的起始位置还是终止位置?
如果只用一个for循环来表示 滑动窗口的起始位置,那么如何遍历剩下的终止位置? 此时难免再次陷入 暴力解法的怪圈。
所以此时for循环里遍历滑动窗口的终止位置 j。
此时如何移动滑动窗口的起始位置呢?如果当前窗口的值大于等于target了,窗口就要向前移动了(也就是该缩小了)。
3. 代码实现
时间复杂度:O(n)
看每一个元素被操作的次数,每个元素在滑动窗后进来操作一次,出去操作一次,每个元素都是被操作两次,所以时间复杂度是 2 × n, 也就是O(n)。
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int i = 0;
int sublen = 0;
int result = INT32_MAX;
int sum = 0;
for (int j = 0; j < nums.size(); j++) {
sum += nums[j];
while (sum >= target) {
sublen = j - i + 1;
result = result < sublen ? result : sublen;
sum -= nums[i];
i++;
}
}
return result == INT32_MAX ? 0 : result;
}
};
易错点:
while(sum>=target) 不是if(sum >=target)
在移动一次 j 后,i 要持续移动使滑动窗口的大小持续减小,直至不满足 sum >= target;
若用 if 只会移动一次 i ,做完判断后直接又开始移动 j 了。
59.螺旋矩阵
给你一个正整数 n ,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。
1. 思路
坚持循环不变量原则 —— 本题中是对每条边的处理规则不变;
采取左闭右开的区间 —— 是指每次处理一条边只处理起始点不处理结束点(结束点也是下条边的起始点);
若n为奇数时 —— 在最后添加一个if 判断语句 if(n%2==1)
2. 代码实现
时间复杂度:O(n^2)
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
int startx = 0, starty = 0;
int offset = 1;
int count = 1;
int i, j;
vector<vector<int>> result(n,vector<int>(n, 0)); // vector定义一个二维数组
int loop = n / 2; // 循环的次数
int mid = n / 2; // 为奇数时的位置坐标
while (loop--) {
for (j = starty; j < n - offset; j++) { // 要先初始化不能在循环里初始,每次开始位置随starty变化
result[startx][j] = count++;
}
for (i = startx; i < n - offset; i++) {
result[i][j] = count++;
}
for (; j > starty; j--) {
result[i][j] = count++;
}
for (; i > startx; i--) {
result[i][j] = count++;
}
startx++;
starty++;
offset++;
}
if (n % 2 == 1) {
result[mid][mid] = count++;
}
return result;
}
};