今天依旧是数组知识的训练,加油
题目:给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
示例 1: 输入:nums = [-4,-1,0,3,10] 输出:[0,1,9,16,100] 解释:平方后,数组变为 [16,1,0,9,100],排序后,数组变为 [0,1,9,16,100]
示例 2: 输入:nums = [-7,-3,2,3,11] 输出:[4,9,9,49,121]
思路1:最先想到的就是最简单的暴力思路,先把平方数组算出来,再排序,最先想到的排序是常见的冒泡排序,但是冒泡排序的时间复杂度为O(n2),运行的时候超时了,有一个用例又长数又大,故转身投靠STL标准库中的sort()函数,快速排序,默认就是从小到大排序,时间复杂度为O(nlogn)。
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;
}
};
同样的,在python中也有可直接使用的排序函数sort()、sorted()
。sort()
只能用于列表,sorted()
用法更为广泛。
sorted()语法:sorted(iterable, cmp=None, key=None, reverse=False)
- iterable:可迭代对象。
- cmp:比较的函数,这个具有两个参数,参数的值都是从可迭代对象中取出,此函数必须遵守的规则为,大于则返回1,小于则返回-1,等于则返回0。
- key:主要是用来进行比较的元素,只有一个参数,具体的函数的参数就是取自于可迭代对象中,指定可迭代对象中的一个元素来进行排序。
- reverse:排序规则,reverse = True 降序 , reverse = False 升序(默认)。
class Solution:
def sortedSquares(self, nums: List[int]) -> List[int]:
return sorted([num*num for num in nums]); # 列表[]为可迭代对象
思路2:这里依然可以使用昨天的双指针做法,(P.S. 双指针的思想实在是太妙了),当然在数组的题目中还是用下标来表示指针。
因为原本的数组也是升序的,只是有负数,所以平方后最大的值一定在两边,中间的值最小。故设两个下标i, j
分别为原数组的头和尾,下标k
为新数组的尾,遍历原数组比较头尾数值的平方和,若nums[i]2 <= nums[j]2 ,就将nums[j]
写入新数组下标k
位置,然后下标k
往前移,下标j
往前移,反之,将nums[i]
写入新数组下标k
位置,然后下标k
往前移,下标i
往后移,遍历结束后,最后返回新数组。代码如下:
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
vector<int> res(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]) {
res[k--] = nums[j] * nums[j];
j--;
}
else {
res[k--] = nums[i] * nums[i];
i++;
}
}
return res;
}
};
同理,也可以用python实现该算法,但不能使用for
循环,因为无法像c++那样去掉第三个表达式,故使用while
循环实现。
class Solution:
def sortedSquares(self, nums: List[int]) -> List[int]:
i = 0
j,k = len(nums) - 1, len(nums) - 1
res = [0] * len(nums) # 新列表
while i <= j:
if nums[i] ** 2 <= nums[j] ** 2:
res[k] = nums[j] ** 2
k -= 1
j -= 1
else :
res[k] = nums[i] ** 2
k -= 1
i += 1
return res
交叉使用两个语言,语法真的很容易混乱。。。
题目:给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。
示例:输入:s = 7, nums = [2,3,1,2,4,3] 输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。
这题我一开始看到,连暴力思路都没有,不知道该如何遍历出所有区间,转头就去看Carl的讲解,滑动窗口还是不熟。
思路:利用滑动窗口寻找最小的子数组。其实也类似于用了两个指针,不过这次是利用两指针中间数值的和来找最小的子数组。
定义滑动窗口起始位置i
和终止位置j
,在终止位置j
不断前移的过程中,如果起始i
到终止j
的之间的数值和大于target
,则先记录当前窗口的长度,然后更新最短长度,并将起始位置i
前移,循环结束返回最短的长度。
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int i = 0;
int res = INT_MAX;
int sum = 0, len = 0;
for (int j = 0; j < nums.size(); j++) {
sum += nums[j];
while (sum >= target) {
len = j - i + 1;
res = res < len ? res : len; // 更新最短的窗口长度
sum -= nums[i++];
}
}
return res == INT_MAX ? 0 : res; // 若长度没变,则说明没找到
}
};
同理可写python版本。
class Solution:
def minSubArrayLen(self, target: int, nums: List[int]) -> int:
i, sum_ = 0, 0
res = len(nums) + 1
for j in range(len(nums)):
sum_ += nums[j]
while sum_ >= target :
res = min(j - i + 1, res) # j-i+1当前窗口长度,更新最小的窗口长度
sum_ -= nums[i]
i += 1
return res if res != len(nums) + 1 else 0
题目:给定一个正整数 n,生成一个包含 1 到 n^2 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵。
示例:输入:3, 输出:[ [ 1, 2, 3 ], [ 8, 9, 4 ], [ 7, 6, 5 ] ]
思路:这题一看就感觉很麻烦,一层一层的循环很容易搞错,好不容易写出来的,还得不到正确答案!
一定要注意循环不变量原则,每一次循环都使用左闭右开区间,才不容易乱,切记!
将每一圈分开来写,每一圈又分为四步,从左到右,从上到小,从右到左,从下到上,每一步都使用左闭右开区间就不不会漏掉或重复,当n
为奇数是,会多出最后一个数在正方形的正中间,最后补上就好。
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
vector<vector<int>> nums(n, vector<int>(n, 0)); // 使用vector定义一个n*n的二维数组
int loop = n / 2;
int start_x = 0, start_y = 0;
int offset = 1;
int num = 1;
while (loop) { // 注意模拟循环的时候不要想着第一轮来写边界条件,到第二轮就出问题了!
int i = start_x;
int j = start_y;
for (j = start_y; j < n - offset; j++) { // 从左往右 左闭右开区间
nums[i][j] = num++;
}
for (i = start_x; i < n - offset; i++) { // 从上往下 左闭右开区间
nums[i][j] = num++;
}
for (; j > offset - 1; j--) { // 从右往左 此时j=i=n-offset 左闭右开区间
nums[i][j] = num++;
}
for (; i > offset - 1; i--) { // 从下往上 此时i=j=offset-1 左闭右开区间
nums[i][j] = num++;
}
offset++; // 进入下一轮循环 边界-1
loop--;
start_x++; // 第二轮循环开始,起始位置都需要+1
start_y++;
}
if ( n % 2 ) {
nums[n / 2][n / 2] = num;
}
return nums;
}
};
真的好容易写乱,理解了思路之后还有很多小的地方需要注意,一定要细心!
因为python的for循环机制和c++不一样,太容易出错了,一定要理解这两种循环机制。
class Solution:
def generateMatrix(self, n: int) -> List[List[int]]:
nums = [[0] * n for _ in range(n)] # 创建一个二维数组
start_x, start_y = 0, 0
loop = n // 2
offset = 1
num = 1
while loop :
i,j = start_x, start_y
for j in range(start_y, n - offset): # 执行完for循环j=n-offset-1,j不等于n-offset,下一个循环不能直接用j
nums[i][j] = num
num += 1
for i in range(start_y, n - offset): # 执行完for循环i=n-offset-1,i不等于n-offset,下一个循环不能直接用i
nums[i][n - offset] = num # 所以j应n-offset
num += 1
for j in range(n - offset, offset - 1, -1): # 执行完for循环j=offset-2,j不等于offset-1,下一个循环不能直接用j
nums[n - offset][j] = num # 所以i应n-offset
num += 1
for i in range(n - offset, offset - 1, -1):
nums[i][offset - 1] = num # 所以j应为offset-1
num += 1
loop -= 1
start_x += 1
start_y += 1
offset += 1
if n % 2:
nums[n // 2][n // 2] = num
return nums
我是照搬c++的思路写的python代码,还是不如Carl老师的简洁,还需继续努力!
遇到数组的题,大概就是这几种方法:二分法,双指针,滑动窗口,模拟行为。两天搞定数组类型的题,跟着Carl老师扎扎实实刷题,收获颇丰!
第一次刷代码随想录,调两种语言的代码还是很吃力,下一次打卡还是专注于c++吧,二刷三刷时再提高难度,挑战自己!