一、LeetCode 977 有序数组的平方
给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
思路1:先每个数求平方和,再排序,时间复杂度取决于排序算法,为nlogn;
#include<iostream>
using namespace std;
int main() {
int n; cin >> n;
int* p = new int[n];
for (int i = 0; i < n; i++) {
cin >> p[i];
}
for (int i = 0; i < n; i++) {
p[i] *= p[i];
}
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < n - i - 1; j++) {
if (p[j] > p[j + 1]) {
int temp = p[j + 1];
p[j + 1] = p[j];
p[j] = temp;
}
}
}
for (int i = 0; i < n; i++) {
cout << p[i] << " ";
}
delete[]p;
return 0;
}
不得不说,vector容器自身功能确实很强大,比array好用很多,但尽量还是减少使用。
时间复杂度O(n+logn)
思路2:双指针移动法,和昨天的习题类似,由于数组自身是有序的,所以最大值一定在两端取到,中间一定是最小值。我们定义一个一样大的数组,比较两端指针指向的值平方大小,再填充到数组里。
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
vector<int>result(nums.size(),0);
int k=nums.size()-1;
for(int i=0,j=nums.size()-1;i<=j;){
if(nums[i]*nums[i]>nums[j]*nums[j]){
result[k--]=nums[i]*nums[i];
i++;
}
if(nums[i]*nums[i]<=nums[j]*nums[j]){
result[k--]=nums[j]*nums[j];
j--;
}
}
return result;
}
};
需要注意的是:for循环里最后没有写i++,j--;这是因为我们是需要进行判断之后才能进行操作。
边界条件i<=j判断一下相等时,i=j,这个元素我们仍要处理。
自身仍然对vector掌握很差,需要弥补。
这里还是说一下,大家不必太在意leetcode上执行用时,打败多少多少用户,这个就是一个玩具,非常不准确。
做题的时候自己能分析出来时间复杂度就可以了,至于leetcode上执行用时,大概看一下就行,只要达到最优的时间复杂度就可以了,一样的代码多提交几次可能就击败百分之百了.....(卡哥说的这句话很好玩)
二、LeetCode209 长度最小的子数组
给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。
数组中另一个重要方法:滑动窗口。本题不好理解,建议隔一阵子自己再做几遍,加深印象。
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int result=2147483647;//方便打擂
int i=0;//作为初始指针
int sum=0;//记录求和
int SubLength;//作为目前区间的长度
for(int j=0;j<nums.size();j++){
sum+=nums[j];
while(sum>=target){
SubLength=j-i+1;
result=result>SubLength?SubLength:result;
sum-=nums[i++];
}
}
return result==2147483647?0:result;
}
};
这份代码有很多需要注意的地方。首先int上限我不会用关键字表示,只能手打21亿
其次为什么用while不用if?这是一个难点。
我们举个例子,假设target=11,而数组是1,1,1,1,11;如果用if表示,循环只会进行一次,但如果我们用while,循环会从1,1,1,1,11一直滑行道11;这样才体现出了滑动窗口,如果是if只能成为1,1,1,11.显然不满足要求。
卡哥的那个动画做的太棒了!
三、LeetCode59 螺旋矩阵
这是一道模拟题,题目本身有多种解法,这里采用循环不变量的原则,不要每次单独判断边界条件,应该作为一个整体去分析思考。
每一条边都坚持左闭右开的原则,模拟二维数组进行处理。
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;
}
};