给你一个按 非递减顺序 排序的整数数组 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 <= nums.length <= 104
-104 <= nums[i] <= 104
nums
已按 非递减顺序 排序进阶:
O(n)
的算法解决本问题解法一:暴力解法,不再赘述
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;
}
};
解法二:双指针法
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
vector<int> newNums;
//找出分界点
//不能写 while(nums[i]<0) i++; 特定输入i会越界,如 nums = [-1]
int negative = -1;
for (int i = 0; i < nums.size(); ++i) {
if (nums[i] < 0) {
negative = i;
} else {
break;
}
}
int i = negative;
int j = i + 1;
while(i>=0 || j<nums.size()){
if(i<0){//左边指针已经走完
newNums.push_back(nums[j]*nums[j]);
j++;
}
else if(j == nums.size()){//右边指针已经走完
newNums.push_back(nums[i]*nums[i]);
i--;
}
else if(nums[i]*nums[i] < nums[j]*nums[j]){//放入较小的数
newNums.push_back(nums[i]*nums[i]);
i--;
}
else{//数字大小相同时,随便放一个数
newNums.push_back(nums[j]*nums[j]);
j++;
}
}
return newNums;
}
};
解法三:优化的双指针法
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
vector<int> newNums(nums.size());
for(int i=0,j=nums.size() - 1, pos = nums.size() - 1;i<=j;pos--){
if(nums[i]*nums[i] > nums[j]*nums[j]){
newNums[pos] = nums[i]*nums[i];
i++;
}
else{
newNums[pos] = nums[j]*nums[j];
j--;
}
}
return newNums;
}
};
给定一个含有 n
个正整数的数组和一个正整数 target
。
找出该数组中满足其总和大于等于 target
的长度最小的 连续
子数组
[numsl, numsl+1, ..., numsr-1, numsr]
,并返回其长度**。**如果不存在符合条件的子数组,返回 0
。
示例 1:
输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。
示例 2:
输入:target = 4, nums = [1,4,4]
输出:1
示例 3:
输入:target = 11, nums = [1,1,1,1,1,1,1,1]
输出:0
提示:
1 <= target <= 109
1 <= nums.length <= 105
1 <= nums[i] <= 105
进阶:
O(n)
时间复杂度的解法, 请尝试设计一个 O(n log(n))
时间复杂度的解法。解法一:暴力解法
class Solution {
public:
int minSubArrayLen(int s, vector<int>& nums) {
int result = INT32_MAX; // 最终的结果
int sum = 0; // 子序列的数值之和
int subLength = 0; // 子序列的长度
for (int i = 0; i < nums.size(); i++) { // 设置子序列起点为i
sum = 0;
for (int j = i; j < nums.size(); j++) { // 设置子序列终止位置为j
sum += nums[j];
if (sum >= s) { // 一旦发现子序列和超过了s,更新result
subLength = j - i + 1; // 取子序列的长度
result = result < subLength ? result : subLength;
break; // 因为我们是找符合条件最短的子序列,所以一旦符合条件就break
}
}
}
// 如果result没有被赋值的话,就返回0,说明没有符合条件的子序列
return result == INT32_MAX ? 0 : result;
}
};
解法二:滑动窗口
涉及求连续的子数组问题时,可以考虑滑动窗口
滑动窗口,其实也是一种双指针。不断调整两个指针之间的距离,得出我们想要的结果
首先要思考 如果用一个for循环,那么参数 i 应该表示 滑动窗口的起始位置,还是终止位置。
如果 i 表示的是起始位置,那么怎么遍历剩下的终止位置呢?另一个指针只能从这个指针的位置开始遍历,那么这又变成了暴力解法
所以,一层for循环的参数应该表示滑动窗口的终止位置
首先慢指针和快指针都指向初始位置。循环开始,快指针向前不断移动,知道经过的数字的和 sum 大于等于 target ,此时移动慢指针,并且不断更新最小数组长度 result 以及经过的数字的和 sum。若某次移动慢指针后的和小于 sum,则继续移动快指针,直到 sum 再次大于 target,如此循环往复
result 的初始值设为一个很大的值(最好是解法一中Int的最大值),如果最后这个值没改变,说明没有符合条件的子序列,返回0
这种方法大大减少了比较子数组的次数,滑动窗口的精妙之处在于根据当前子序列和大小的情况,不断调节子序列的起始位置。从而将O(n^2)暴力解法降为O(n)。
不要以为for里放一个while就以为是O(n2), 主要是看每一个元素被操作的次数,每个元素在滑动后进来操作一次,出去操作一次,每个元素都是被操作两次,所以时间复杂度是 2 × n 也就是O(n)。
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int i = 0,result = 1000000000,sum = 0;
for(int j=0;j<nums.size();j++){
sum += nums[j];
while(sum >= target){
int subLen = j - i + 1;
result = min(result,subLen);
sum -= nums[i];
i++;
}
}
return result == 1000000000 ? 0 : result;
}
};