今天刷了一道难度适中的题目,感觉还凑合,下面就来总结一下经验吧。
具体题目如下:
Given an array of n positive integers and a positive integer s, find the minimal length of a contiguous subarray of which the sum ≥ s. If there isn't one, return 0 instead.
Example:
Input: s = 7, nums = [2,3,1,2,4,3]
Output: 2
Explanation: the subarray [4,3] has the minimal length under the problem constraint.
Follow up:
If you have figured out the O(n) solution, try coding another solution of which the time complexity is O(n log n).
题意分析:
给定一个正整数数组和一个正数s,然后在该数组中找到元素之和≥s的最短连续子数组并返回最短连续子数组的长度,若没有这样的连续子数组,请返回0。如果你已经找到了O(n)的解决方法,请尝试编写时间复杂度为O(n log n)的其他解决方法。
解答如下:
方法一(滑动窗格法①)
用两个指针l,r分别指向左端,同时确保两指针构成的区间没有元素,然后将sum与s进行比较,当满足r+1 < nums.size() && sum < s时,右端指针r++且sum=sum+nums[r],否则l++且sum=sum-nums[l],紧接着再判断sum≥s,如果是的则执行minlen = min(minlen, r-l+1),直到l < nums.size()不满足时,跳出循环再判断if (minlen == nums.size()+1),如果满足则说明没有这样的连续子数组故返回0,否则返回所找的最短的连续子数组。
这里有几点需要注意:① l = 0,r = -1,是为了保证初始区间中没有元素。② 程序中用到了if else的嵌套,而不用两个if if 是为了避免本来sum<s加了以后sum>=s,此时再进行判断就会造成sum<s,那么对于后面记录最短连续子数组的长度的if是有影响的。③if(r+1 < nums.size() && sum < s),该判断中之所以加r+1 < nums.size()是为了防止右边界发生数组越界。随着r的增大,连续子数组始终处于前增后减的状态,但是当r+1 < nums.size()不满足时,连续子数组会处于前不增但后会减的状态,直至l < nums.size()不满足,此时r指向数组最后一个元素,l为nums.size()已发生越界,所构成的区间也不包含任何元素。
class Solution {
public:
int minSubArrayLen(int s, vector<int>& nums) {
int l = 0;
int r = -1;
int sum = 0;
int minlen = nums.size()+1;//为了避免数组本身就是最短连续子数组,所以这里不能与数组最大长度一样
while(l < nums.size()){
if(r+1 < nums.size() && sum < s) //r+1 < nums.size()防止数组越界
sum += nums[++r];
else //此处是不可以改成if的,否则会影响到下面那个if
sum -= nums[l++];
if(sum >= s)
minlen = min(minlen, r-l+1);
}
if (minlen == nums.size()+1)
return 0;
return minlen;
}
};.
提交后的结果如下:
方法二(滑动窗格法②)
用for循环向前遍历元素并进行累加,用while循环判断sum >= s是否满足,若满足则记录下该子连续数组的长度,且从该子数组的左端删除元素,直到sum >= s不满足时为止。
//时间复杂度:O(n)
class Solution {
public:
int minSubArrayLen(int s, vector<int>& nums) {
int n = nums.size(), start = 0, sum = 0, minlen = nums.size()+1;
for (int i = 0; i < n; i++) {
sum += nums[i];
while (sum >= s) { //不断减,直至不满足条件,这里不可以改为if
minlen = min(minlen, i - start + 1);
sum -= nums[start++];
}
}
return minlen == nums.size()+1 ? 0 : minlen; //简化了if和else判读,如果minlen == nums.size()+1 满足,则选择0,如果minlen == nums.size()+1 不满足则选择minlen
}
};
提交后的结果如下:
方法三(暴力解法①)
用两层for循环,遍历所有可能的连续子数组,逐一计算逐一排除,然后得到最终答案。
class Solution {
public:
int minSubArrayLen(int s, vector<int>& nums) {
int sum = 0;
int minlen = nums.size()+1;
for (int i = 0; i < nums.size() ; i++) {
for (int j = i; j < nums.size(); j++) {
sum += nums[j];
if(sum >= s )
minlen = min(minlen, j-i+1);
}
sum = 0; //每次都必须清零,不然sum值会越来越大
}
if (minlen == nums.size()+1)
return 0;
return minlen;
}
};
提交后的结果如下:
方法四(暴力解法②)
用三层for循环,其中最内层for循环用于计算连续子数组的和。三层for循环的解法可能会Time Limit Exceeded。
// 时间复杂度: O(n^3)
// 空间复杂度: O(1)
class Solution {
public:
int minSubArrayLen(int s, vector<int>& nums) {
assert(s > 0);
int minlen = nums.size() + 1;
for(int l = 0 ; l < nums.size() ; l ++)
for(int r = l ; r < nums.size() ; r ++){
int sum = 0;
for(int i = l ; i <= r ; i ++)
sum += nums[i];
if(sum >= s)
minlen = min(minlen, r - l + 1);
}
if(minlen == nums.size() + 1)
return 0;
return minlen;
}
};
提交后的结果如下:
日积月累,与君共进,增增小结,未完待续。