Given an array of n positive integers and a positive integer s, find the minimal length of a subarray of which the sum ≥ s. If there isn't one, return 0 instead.
For example, given the array [2,3,1,2,4,3] and s = 7,
the subarray [4,3] has the minimal length under the problem constraint.
題意
給出一個包含n個正整數(shù)的數(shù)組和一個正整數(shù)s晰赞,找到長度最小的(連續(xù))子數(shù)組使其和大于等于s。如果不存在這樣的子數(shù)組府蛇,返回0唐责。
比如數(shù)組為[2, 3, 1, 2, 4, 3]莱革,s = 7。子數(shù)組[4, 3]是長度最小的子數(shù)組,其和4+3≥7埠对。
分析
使用一種在線處理的方法术健,類似“數(shù)組的最大子數(shù)組和”的O(n)解法汹碱。
步驟
- 我們設(shè)置bottom和top控制子數(shù)組的頭部和尾部。
- 初始化bottom=0荞估,top為-1咳促,表示一個空的子數(shù)組稚新。
- 子數(shù)組和sum=0,最小長度len=0跪腹。
- 當(dāng)sum < s時褂删,在當(dāng)前子數(shù)組的尾部增加一個元素bottom[++top]。
- 當(dāng)sum ≥ s時冲茸,去掉當(dāng)前子數(shù)組的頭部元素屯阀,并++bottom。
- 退出循環(huán)的條件:top == nums.size() 或 bottom>top(此時已經(jīng)存在最小len為1轴术,不可能更小难衰,可以退出)。
算法復(fù)雜度
由于top和bottom至多遍歷一次數(shù)組nums膳音,因此算法復(fù)雜度為O(n)召衔。
更多練習(xí)
題目要求再給出一種O(nlogn)的解法。
簡略分析
采用分治法的思想祭陷。每次將區(qū)間A一分為二苍凛,成為A1和A2。子問題是求兩個子區(qū)間A1和A2中的各自的最小子數(shù)組長度len1和len2兵志,以及區(qū)間A的最小子數(shù)組長度len中的最小值醇蝴,即min{len1, len2, len}。
算法復(fù)雜度
由主定理(master定理)可知:T(n) = 2T(n/2) + n想罕,故算法復(fù)雜度為O(nlogn)*悠栓。
AC代碼
O(n)及O(nlogn)算法
//O(n)
class Solution {
public:
int minSubArrayLen(int s, vector<int>& nums) {
if (!nums.size()) return 0;
int bottom = 0, top = -1;
int sum = 0, len = 0;
while (true) {
if (sum < s) {
++top;
if (top != nums.size())
sum += nums[top];
else
break;
} else {
sum -= nums[bottom]; ++bottom;
if (bottom > top)
break;
}
if (sum >= s) {
int new_len = top - bottom + 1;
if (!len || len && new_len < len)
len = new_len;
}
}
return len;
}
};
//O(nlogn)
class Solution {
public:
int MAX_INT = 999999999;
int minSubArrayLen(int s, vector<int>& nums) {
if (!nums.size()) return 0;
return findMinLen(s, nums, 0, nums.size() - 1);
}
int findMinLen(int s, vector<int>& nums, int bottom, int top) {
if (top == bottom) return nums[top] >= s ? 1 : 0;
int mid = (bottom + top) / 2;
int left = mid, right = mid + 1, sum = 0, len;
while (sum < s && (right <= top || left >= bottom)) {
if (right > top) {
sum += nums[left]; --left;
}
else if (left < bottom) {
sum += nums[right]; ++right;
}
else if (nums[left] > nums[right]) {
sum += nums[left]; --left;
}
else {
sum += nums[right]; ++right;
}
}
if (sum >= s) {
len = right - left - 1;
int leftLen = findMinLen(s, nums, bottom, mid);
int rightLen = findMinLen(s, nums, mid + 1, top);
return minValue(leftLen, rightLen, len);
}
else {
return 0;
}
}
int minValue(int x, int y, int z) {
if (!x) x = MAX_INT;
if (!y) y = MAX_INT;
if (x <= y && x <= z) return x;
if (y <= x && y <= z) return y;
return z;
}
};