导航
今天这篇主要讲述leetcode题目:
977.有序数组的平方
209.长度最小的子数组
59.螺旋矩阵II
有序数组的平方
题目链接: 977.有序数组的平方
分析过程:
本题应该使用双指针的思路来解答。我的思路是找到绝对值最小值,从其开始往左右移动,每次移动判断左右指针的绝对值,选择就绝对值最小的值,并令指针移动,直到抵达数组两边缘。
时间复杂度:O(n) 空间复杂度O(n)
以下是实现过程代码:
vector<int> sortedSquares(vector<int>& nums)
{
// 怎么写最大值
// c <limits.h> INT_MAX
// c++ <limits> std::numeric_limits<int>::max()
if (nums.empty()) return nums;
auto myPingfang = [] (int val) {
return val * val;
};
vector<int> ret;
// ret.reserve(nums.size());
ret.resize(nums.size());
int qbsMin = std::numeric_limits<int>::max();
int left = 0;
for (int i = 0; i < nums.size(); ++i) {
if (std::abs(nums[i]) < qbsMin) {
qbsMin = std::abs(nums[i]);
left = i;
}
}
int right = left + 1;
// 特殊情况
if (left > 0 && left < nums.size() - 1) {
if (std::abs(nums[left - 1]) < std::abs(nums[left + 1])){
right = left;
left = left - 1;
}
}
int index = 0;
while (left >= 0 || right < nums.size()) {
if (left >= 0 && right < nums.size()) {
if (std::abs(nums[left]) >= std::abs(nums[right])) {
ret[index++] = myPingfang(nums[right]);
right++;
} else {
ret[index++] = myPingfang(nums[left]);
left--;
}
continue;
}
if (left >= 0) {
ret[index++] = myPingfang(nums[left]);
left--;
}
if (right < nums.size()) {
ret[index++] = myPingfang(nums[right]);
right++;
}
}
return ret;
}
代码比较冗余,经过查找资料,可以简化思路如下:
双指针从数组两端向中间靠拢,计算平方并比较大小,并把较大元素放置到数组末端
vector<int> LCFuntion::sortedSquares2(vector<int> &nums)
{
vector<int> ret(nums.size(), 0);
int right = nums.size() - 1;
int index = nums.size() - 1;
for (int left = 0; left <= right;) {
if (std::abs(nums[left]) > std::abs(nums[right])) {
ret[index--] = nums[left] * nums[left];
left++;
} else {
ret[index--] = nums[right] * nums[right];
right--;
}
}
return ret;
}
长度最小的子数组
题目链接: 209.长度最小的子数组
分析过程:
最简单的解法是暴力遍历,思路是从子数组长度为1到n,分别遍历数组累加,直到找到满足条件时退出遍历,返回结果。
最坏的情况是长度为数组长度n时,时间复杂度为O(n^2)
其实现过程如下:
int minLengthSubArray(vector<int> &nums, int target)
{
for (int i = 1; i <= nums.size(); ++i) {
int valSize = i;
for (int j = 0; j < nums.size() - valSize + 1; ++j) {
int sum = 0;
for (int z = 0; z < valSize; ++z)
sum += nums[j + z];
if (sum >= target)
return valSize;
}
}
return 0;
}
那有没有更高效的解法呢?答案是有的
滑动窗口解法:参考文章
滑动窗口的解法是通过一个维护一个滑动窗口,只要遍历一遍数据,不断调整窗口大小,更新最优解。
int minLengthSubArray2(vector<int> &nums, int target)
{
// [begin, j] 是窗口,begin窗口起点,j终点
int result = INT_MAX;
int begin = 0;
int sum = 0;
for (auto j = 0; j < nums.size(); ++j) {
sum += nums[j];
while (sum >= target) {
int length = j - begin + 1;
result = length < result ? length : result;
sum -= nums[begin];
begin++;
}
}
return result == INT_MAX ? 0 : result;
}
在使用滑动窗口时,需要确定三个要素:
- 窗口是什么?
- 窗口的左边界如何移动?
- 窗口的右边界如何移动?
本题里 窗口 是满足和大于等于s的子数组,如果子数组和大于等于s了,就需要移动左边界了【即减少窗口大小】,如果数字和小于s,那么就需要移动右边界【增加窗口大小】,就是在这动态的调整窗口过程中,去更新 和大于等于s的子数组 的最优解。
螺旋矩阵II
题目描述: 59.螺旋矩阵II
这个题目的解法需要考虑的边界情况较多,需要仔细了
思路是从最外圈填充元素开始,缩小圈子,直到填充到矩形中心。
vector<vector<int> > turnRect(int n)
{
vector<vector<int>> ret(n, vector<int>(n, 0));
int val = 0;
for (int i = 0; i < n / 2; ++i) {
// [0,0] - [n,0]
for (int j = 0; j < n - i; j++) {
ret[j][i] = val++;
}
// [n,0] - [n, n]
for (int j = 1; j < n - i; ++j) {
ret[n - 1 - i][j] = val++;
}
// [n, n] - [0, n]
for (int j = n - 2 - i; j >= i; --j) {
ret[j][n - 1 - i] = val++;
}
for (int j = n - 2 - i - 1; j > i; --j) {
ret[i][j] = val++;
}
// [0, n] - [0, 0]
}
return ret;
}
总结
- vector中使用时区分reserve 和 resize ,reserve后直接[]访问元素是会崩溃的,原因是reserve并没有增加size大小,只是分配好空间,增长是capacity大小
- 做长度最小的子数组 题目时发现思路有点问题,思路一开始是对的,使用双指针,但是指针从哪开始没有想清楚
- sqrt当成平分了,真是自己傻了,题目看起来简单,却耗费了1个多小时才完全作对