題目
給定一個含有 n 個正整數的數組和一個正整數 s ,找出該數組中滿足其和 ≥ s 的長度最小的連續子數組,並返回其長度。如果不存在符合條件的連續子數組,返回 0。
示例:
輸入:s = 7, nums = [2,3,1,2,4,3]
輸出:2
解釋:子數組 [4,3] 是該條件下的長度最小的連續子數組。
思路分析
暴力法
最先想到的自然是暴力法,這也是正常的思路。我認爲許多算法問題都是對暴力搜索的優化,這幾次參加leetcode周賽感受很深刻。每次做題都是暴力搜索之後超時,優化的步驟每次都有問題。對於該題暴力法的思路如下,兩次循環遍歷所有可能的連續子數組,然後計算出每個連續子數組的和,該方法的時間複雜度位O(n3)。顯然可以事先計算每段(0, i)的和 dp[i],然後再計算連續子數據的和時,其時間複雜度優化爲O(1),總的時間複雜度變爲O(n2)這是很容易就能想到的優化方法。代碼如下:
class Solution {
public int minSubArrayLen(int s, int[] nums) {
int len = nums.length;
if (len == 0) return 0;
int[] dp = new int[len];
dp[0] = nums[0];
for (int i = 1; i < len; i++) {
dp[i] = dp[i-1] + nums[i];
}
if (dp[len - 1] < s) return 0;
int res = len;
for (int i = 0; i < len; i++) {
for (int j = i; j < len; j++) {
if (dp[j] - dp[i] + nums[i] >= s) {
if (j - i + 1 < res) {
res = j - i + 1;
}
}
}
}
return res;
}
}
利用二分搜索進行優化
在循環的內層,是一個遍歷操作,時間複雜度爲O(n)。目標是找出一個 j 使得:
同樣可以轉換爲找到一個 j 使得:
即找到一個大於等於某值 target 的 dp[j],顯然這可以利用二分搜索進行查找。
這是一個典型的二分搜索程序:
private int binarySearch(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] == target) {
return mid;
} else if (nums[mid] < target) {
left = mid + 1;
} else
right = mid - 1;
}
return -1;
}
要對二分搜索有一定的理解:
- 該二分搜索結束時,left 和 right 有兩種情況。找到了target,此時 left=right ;未找到target,此時left < right。
- 未找到target時,nums[left] 爲第一個大於target的值,nums[right] 爲第一個小於target的值。
- 當target不在nums的(min, max) 範圍內時,沒有大於或者小於target的nums[i],此時left = nums.length 或者 right = -1。同樣,有一種更加直觀的理解,right 在target 值所在位置的左邊,left 在target值所在位置的右邊。
基於以上理解,left就是大於等於target值的索引。此時,left 等於 nums.length 時,說明該索引不合法;left < nums.length 時,該索引合法,能找到一個大於等於某值target的 dp[j]。其餘步驟較爲簡單,代碼如下:
class Solution {
public int minSubArrayLen(int s, int[] nums) {
int len = nums.length;
if (len == 0) return 0;
int[] dp = new int[len];
dp[0] = nums[0];
for (int i = 1; i < len; i++) {
dp[i] = dp[i-1] + nums[i];
}
if (dp[len - 1] < s) return 0; // 如果不存在
int res = len;
for (int i = 0; i < len; i++) {
// 從 i 到 j : dp[j] - dp[i] + nums[i] >= s
int target = s + dp[i] - nums[i];
// 找到大於等於 target的那個j
int j = binarySearch(dp, target);
if (j < len) {
if (j - i + 1 < res)
res = j - i + 1;
}
}
return res;
}
private int binarySearch(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] == target) {
return mid;
} else if (nums[mid] < target) {
left = mid + 1;
} else
right = mid - 1;
}
return left;
}
}