代码随想录算法训练营第二天 | LeetCode977.有序数组的平方、LeetCode209.长度最小的子数组、LeetCode59.螺旋矩阵II、总结
977. 有序数组的平方
题目链接:977. 有序数组的平方
思路:
- 在原始数组里,绝对值最大的数就在数组两端,所以乘方最大的数在数组两端
- 使用双指针思想,将数组两端绝对值较大的数,塞入要输出的数组中
- 两指针相遇循环结束
- 塞入方式为尾插法,塞完后数组是递减的,所以要再反转一下变成递增
时间复杂度:O(2n)
,感觉这么说准确一点,便利原始数组复杂度是O(n)
,反转数组也是O(n)
空间复杂度O(n)
代码:
class Solution
{
public:
vector<int> sortedSquares(vector<int> &nums)
{
std::vector<int> res;
int length = nums.size();
int front = 0;
int back = length - 1;
while (front <= back)
{
// 找数组两侧绝对值更大的数
// 一定就是当前数组中乘方值最大的数
if (abs(nums[front]) > abs(nums[back]))
{
res.push_back(nums[front] * nums[front]);
++front;
}
else
{
res.push_back(nums[back] * nums[back]);
--back;
}
}
// 当前数组是一个(非单调)递减数组,需要反转
std::reverse(res.begin(),res.end());
return res;
}
};
总结:
- 不算太难,比较容易想到,美中不足是输出数组的长度是确定的
- 其实可以直接初始化数组,然后从后向前塞入数据
- 这样可以节省一次反转的时间复杂度
- 做题体验:
哈哈
(哈哈
>嘿嘿
>哎哎
>嘤嘤
) - 时间:
未计
一刷
209. 长度最小的子数组
题目链接:209. 长度最小的子数组
思路:
- 该题为了实现
O(n)
的时间复杂度,用了j作为连续数组头,j为连续数组尾 - 先递增j累加数组的值,当值大于等于target的时候
- 开始试试看把i指向的尾巴删了能不能满足,可以删一个就再删第二个试试
- 当和比target小的时候再移动j
- 对每种满足的情况记下j和i的差值,代表数组长度,最后输出最小值
时间复杂度:O(n)
,空间复杂度O(1)
代码:
class Solution
{
public:
// 滑动窗口:
int minSubArrayLen(int target, vector<int> &nums)
{
int i = 0;
auto result = INT32_MAX;
int sum = 0;
for (int j = 0; j < nums.size(); j++)
{
sum += nums[j];
while (sum >= target)
{
int sum_l = j - i + 1;
result = std::min(result, sum_l);
sum -= nums[i];
++i;
}
}
if (i == 0 && sum < target)
return 0;
return result;
}
};
总结:
- 除了暴力法没想出来解法,看了cral哥的视频,算是滑动窗口初体验
- 做题体验:
嘤嘤
(哈哈
>嘿嘿
>哎哎
>嘤嘤
) - 时间:
未计
一刷
59. 螺旋矩阵 II
题目链接:59. 螺旋矩阵 II
思路:
- 我这个代码的思路和carl哥有所出入,记下来以免自己以后忘了
- 我在代码里将数组的写入分为四种阶段,并且是依序变化的
- 向右、向下、向左、向右
- 数组越界或者写入位置已有数值都会使方向改变
- 输出完n*n个数之后就结束
时间复杂度:O(n)
,空间复杂度O(1)
,这个应该算原地吧,输出不能比这更小了
代码:
class Solution
{
public:
enum zhuanquan // 一个枚举类,四个写入方向是依序变化的
{
right = 0,
down,
left,
up
};
vector<vector<int>> generateMatrix(int n)
{
vector<vector<int>> res(n, vector<int>(n, 0));
int direction = 0; // 和枚举配合使用,0代表向右,以此类推
int count = 1; // 要写入的指
int target = n * n; //
int i = 0; // 写入的初始行
int j = 0; // 写入的初始列
while (count <= target)
{
switch (direction)
{
case right: // 以向右写入为例
{
if (j >= n) // 判断是否越界
{
direction = (direction + 1) % 4; // 改变方向
++i; // 进一行(下一个方向写入的起始位置)
--j; // 退一列(上一轮循环多加了)
}
else if (res[i][j] != 0) // 判断是否已写入数据
{
direction = (direction + 1) % 4;
++i;
--j;
}
else
{
res[i][j] = count++;
++j;
}
break;
}
case down:
{
if (i >= n)
{
direction = (direction + 1) % 4;
--j;
--i;
}
else if (res[i][j] != 0)
{
direction = (direction + 1) % 4;
--j;
--i;
}
else
{
res[i][j] = count++;
++i;
}
break;
}
case left:
{
if (j < 0)
{
direction = (direction + 1) % 4;
--i;
++j;
}
else if (res[i][j] != 0)
{
direction = (direction + 1) % 4;
--i;
++j;
}
else
{
res[i][j] = count++;
--j;
}
break;
}
case up:
{
if (i < 0)
{
direction = (direction + 1) % 4;
++j;
++i;
}
else if (res[i][j] != 0)
{
direction = (direction + 1) % 4;
++j;
++i;
}
else
{
res[i][j] = count++;
--i;
}
break;
}
default:
break;
}
}
return res;
}
};
总结:
- [TODO]有空按照carl哥的思路手敲一遍
- 做题体验:
嘿嘿
(哈哈
>嘿嘿
>哎哎
>嘤嘤
) - 时间:
未计
一刷
总结
个人感觉carl哥的这个题目列表中,数组的题目都以双指针为主,此外还遇到了一道滑动窗口的题目,慢慢做吧,以后对做题的理解肯能会更深刻