声明:本账号博客完全是出于个人学习以及打卡目的,无意涉及任何利益。涉及引用的文章和代码都会尽量附上出处,以示尊重和感谢。
今日任务:
Part_1 力扣977.有序数组的平方
题目描述:
给你一个按 非递减顺序 排序的整数数组 nums
,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/squares-of-a-sorted-array
个人最初解答:
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
vector<int>re;
for (int i = 0; i < nums.size(); ++i) {
re.push_back(nums[i] * nums[i]);
}
sort(re.begin(), re.end());
return re;
}
};
初步观察发现有相应函数可以直接调用,不过时间和空间复杂度有些高,力扣显示击败率大概都在20%左右。出于学习的目的,完成其他题目后,还是回来从算法角度做了一下,先附上卡哥的双指针。
卡哥解答:(代码引用自力扣评论,感谢卡哥)
class Solution {
public:
vector<int> sortedSquares(vector<int>& A) {
int k = A.size() - 1;
vector<int> result(A.size(), 0);
for (int i = 0, j = A.size() - 1; i <= j;) { // 注意这里要i <= j,因为最后要处理两个元素
if (A[i] * A[i] < A[j] * A[j]) {
result[k--] = A[j] * A[j];
j--;
}
else {
result[k--] = A[i] * A[i];
i++;
}
}
return result;
}
};
然后再简单描述一下个人思路。有些类似,同样是使用双指针,不过遇到的情况比较复杂。首先找出最小平方项的下标,并填为首项;然后从最小平方项出发,向两侧双指针(实际未必存在双侧),取小填入容器。由此发现,本题卡哥解答的妙处不仅仅是双指针,还有k--,即反向填入容器,这一步避免了很多弯路。由于个人技术尚不熟练,中间出发的双指针做法还有一些问题没有解决,所以暂时就不发在这里了,今天下午(哈哈,现在02:15,确实是今天下午)再认真检查一下,然后补发在评论区。
Part_2 力扣209.长度最小的子数组
题目描述:
给定一个含有 n 个正整数的数组和一个正整数 target 。
找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/minimum-size-subarray-sum
由于本人之前稍微看过一些代码随想录,对滑动窗口有些印象,所以思路上没有很多需要额外补充,这里直接附上卡哥的原版滑动窗口。
卡哥解答:(代码引用自力扣评论,感谢卡哥)
class Solution {
public:
int minSubArrayLen(int s, vector<int>& nums) {
int result = INT32_MAX;
int sum = 0; // 滑动窗口数值之和
int i = 0; // 滑动窗口起始位置
int subLength = 0; // 滑动窗口的长度
for (int j = 0; j < nums.size(); j++) {
sum += nums[j];
// 注意这里使用while,每次更新 i(起始位置),并不断比较子序列是否符合条件
while (sum >= s) {
subLength = (j - i + 1); // 取子序列的长度
result = result < subLength ? result : subLength;
sum -= nums[i++]; // 这里体现出滑动窗口的精髓之处,不断变更i(子序列的起始位置)
}
}
// 如果result没有被赋值的话,就返回0,说明没有符合条件的子序列
return result == INT32_MAX ? 0 : result;
}
};
接下来,对卡哥这段代码简单补充一些注释,作为滑动窗口主体思路以外值得注意的细节。
result = result < k ? result : k;//记录k的最小值
return result == INT32_MAX ? 0 : result;//若未sum >= target,则result = INT32_MAX
Part_3 力扣59.螺旋矩阵II
题目描述:
给你一个正整数 n
,生成一个包含 1
到 n2
所有元素,且元素按顺时针顺序螺旋排列的 n x n
正方形矩阵 matrix
。
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/spiral-matrix-ii
本题难度标记为中等,原因可能在于初步观察其实际解答思路,似乎并没有太多可操作空间,大多可以想到逐层击破,若进一步探索,或许可以从数学的角度在通项上找到更好的规律。然而卡哥一句“左闭右开”可以使解答过程工整许多。
卡哥解答:(代码引用自力扣评论,感谢卡哥)
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
vector<vector<int>> res(n, vector<int>(n, 0)); // 使用vector定义一个二维数组
int startx = 0, starty = 0; // 定义每循环一个圈的起始位置
int loop = n / 2; // 每个圈循环几次,例如n为奇数3,那么loop = 1 只是循环一圈,矩阵中间的值需要单独处理
int mid = n / 2; // 矩阵中间的位置,例如:n为3, 中间的位置就是(1,1),n为5,中间位置为(2, 2)
int count = 1; // 用来给矩阵中每一个空格赋值
int offset = 1; // 需要控制每一条边遍历的长度,每次循环右边界收缩一位
int i,j;
while (loop --) {
i = startx;
j = starty;
// 下面开始的四个for就是模拟转了一圈
// 模拟填充上行从左到右(左闭右开)
for (j = starty; j < n - offset; j++) {
res[startx][j] = count++;
}
// 模拟填充右列从上到下(左闭右开)
for (i = startx; i < n - offset; i++) {
res[i][j] = count++;
}
// 模拟填充下行从右到左(左闭右开)
for (; j > starty; j--) {
res[i][j] = count++;
}
// 模拟填充左列从下到上(左闭右开)
for (; i > startx; i--) {
res[i][j] = count++;
}
// 第二圈开始的时候,起始位置要各自加1, 例如:第一圈起始位置是(0, 0),第二圈起始位置是(1, 1)
startx++;
starty++;
// offset 控制每一圈里每一条边遍历的长度
offset += 1;
}
// 如果n为奇数的话,需要单独给矩阵最中间的位置赋值
if (n % 2) {
res[mid][mid] = count;
}
return res;
}
};
由于本人vector的应用并不熟练,因此前面初始化直接借用了卡哥的解答,不过后面尝试基于个人理解做出一些改动,绝对称不上是改进,权当个人理解。不难想到或者观察到卡哥解答中的startx、starty、offset有很多相同点,因此结合一些个人理解,直接合并为如下的z,相信细心的各位一定已经注意到其区别,即可解释性。同时,本人也尝试将卡哥的已经足够工整的左闭右开稍微调整,不过算法复杂度上并无变化,只是结合了一些个人对于涉及变量的理解。
个人解答:
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
vector<vector<int>> res(n, vector<int>(n, 0));
int z = 0;
int c = 1;
int loop = n / 2;
int mid = n / 2;
while (z < loop) {
int i = z;
int j = z;
for (; j < n - z - 1; j++) res[z][j] = c++;//上行
for (; i < n - z - 1; i++) res[i][j] = c++;//右列
for (; j > z; j--) res[i][j] = c++;//下行
for (; i > z; i--) res[i][j] = c++;//左列
z++;
}
if (n % 2 == 1) res[mid][mid] = n * n;
return res;
}
};
今日感悟:
由于之前看过一些代码随想录,这部分曾经对我感触颇深,因此时隔数月竟然还依稀有些印象,所以在解答思路的原创性方面并不侧重,不过希望能尽量独立复现一些主体思路,如双指针、滑动窗口等,在深化理解的同时也开始尝试诸如双指针的中间出发形式等,虽然复杂度上并不一定等同,何况改善。但对于代码随想录的学习绝对不只一轮,所以每一次的思考都值得肯定,并为之付出时间尝试。