題目描述:
這個題目一開始我沒有看清題目中要求是“連續”的子數組,因此最早想到的方法是講整個數組排序,如果最大的數滿足>=s,則輸出1,最大加第二大的數滿足,則輸出2.遇到這個測試用例時報錯
s = 213, [12,28,83,4,25,26,25,2,25,25,25,12]
明確題意後,首先想到的方法是1.暴力法,兩個指針,遍歷幾乎所有情況,代碼如下:
class Solution {
public:
int minSubArrayLen(int s, vector<int>& nums) {
int slow=0,fast=0, minlen=10000,templen=255; //一個慢指針一個快指針
for(;slow<nums.size();slow++) //慢指針從0開始
{
int sum=0; //每開始一個慢指針,重置當前的和爲0
for(fast=slow;fast<nums.size();fast++) //快指針從慢指針開始往後加
{
sum+=nums[fast];
if(sum>=s) //滿足要求
{
templen = fast- slow + 1; //當前長度計算
if(templen<minlen) minlen=templen; //如果小於最小長度,更新。這兩行可以用一行min函數的代碼搞定
}
}
}
if(templen==255) return 0; //沒有滿足要求的情況,返回
else return minlen;
}
};
看了下官方題解說的暴力法,時間複雜度有O(n3),我這種暴力法應該屬於改良過的,時間複雜度應該只有O(n2),測試結果從執行用時上看很慘淡,說明肯定有更好的方法;
第二種方法2.滑動窗口法
這種方法算法課上老師有提到過,這次終於碰到實戰應用,寫了一下確實很快,總的來說就是一前一後雙指針,形成了一個方框,所有的數加起來後如果<s,說明不存在滿足要求的數組,返回0;如果總和>=s,則加左指針可使總和減小,在此期間探到最小的數組長度,代碼如下:
class Solution {
public:
int minSubArrayLen(int s, vector<int>& nums) {
int left = 0, right = 0, sum = 0, minlen = 10000;
while (left < nums.size()&& right<nums.size())
{
if (sum >= s) //總和大於S的情況,左指針右移
{
minlen = min(minlen, right - left);
sum -= nums[left];
left++;
}
else //總和小魚S的情況,右指針右移
{
sum += nums[right]; //先加右指針的數再把右指針右移,因此右指針屬於開區間
right++;
}
}
while (sum >= s&&left<nums.size()) //由於右指針開區間,存在越界風險,這裏討論的是已經加到最後一位的情況
{
minlen = min(minlen, right - left);
sum -= nums[left];
left++;
}
if (minlen == 10000) return 0;
else return minlen;
}
};
這裏的代碼可以再簡潔一點就是如果先加nums[right]再right++的話就不用擔心right越界的情況
還有第3中方法叫二分查找法,引進了一個新的sum數組,第i個數表示nums數組前i項的和,該方法我個人認爲比較複雜,將找最小數組的問題轉爲找一個臨界值的問題,從而可以用常用的更快的查找方法來加快速度,這裏就不寫了。