为什么80%的码农都做不了架构师?>>>
问题:
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.
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.
More practice:
If you have figured out the O(n) solution, try coding another solution of which the time complexity is O(n log n).
解决:
① 使用双指针从头遍历。时间复杂度O(n)。
class Solution {//3ms
public int minSubArrayLen(int s, int[] nums) {//不能用map,顺序会改变
if(nums == null || nums.length == 0) return 0;
int min = Integer.MAX_VALUE;
int count = 0;
for (int i = 1;i < nums.length;i ++){//记录从头开始到当前位置的和
nums[i] = nums[i] + nums[i - 1];
}
if(s > nums[nums.length - 1]) return 0;
if(s == nums[nums.length - 1]) return nums.length;
int p1 = 0;
int p2 = -1;//使用双指针遍历数组
while(p1 < nums.length){
while(nums[p1] < s){
p1 ++;
}
if(p2 == -1){//记录第一次达到s值时的值的个数
count = p1 + 1;
if(min > count) min = count;
p2 ++;
}
while(nums[p1] - nums[p2 + 1] >= s){
p2 ++;
}
if(nums[p1] - nums[p2] >= s){//找到满足条件的点,记录个数
count = p1 - p2;
}
if (min > count) min = count;
p1 ++;
}
return min;
}
}
② 看了discuss中的解法,感觉自己想复杂了。使用了滑动窗口的机制。时间复杂度O(n)。
class Solution { //2ms
public int minSubArrayLen(int s, int[] nums) {
int min = Integer.MAX_VALUE;
int i = 0;
int sum = 0;
for (int j = 0; j < nums.length; j++) {
sum += nums[j];
while (sum >= s) {
min = Math.min(min, j - i + 1);
sum -= nums[i ++];
}
}
return (i == 0 && sum < s) ? 0 : min;
}
}
③ 更多练习的部分,实现时间复杂度为O(nlogn)的算法。使用折半查找,搜索是否存在满足条件的大小为k的窗口。
class Solution { //7ms
public int minSubArrayLen(int s, int[] nums) {
int i = 1;//因为表示的是满足条件的数值的长度,所以从1开始,最长为数组长度。
int j = nums.length;
int min = 0;
while(i <= j){
int mid = (i + j) / 2;
if (existWindow(mid,nums,s)){//如果存在满足条件的小于等于mid的窗口长度。
j = mid - 1;
min = mid;
}else{
i = mid + 1;
}
}
return min;
}
public boolean existWindow(int k,int[] nums,int s){
int sum = 0;
for (int i = 0;i < nums.length;i ++){
if (i >= k) sum -= nums[i - k];
sum += nums[i];
if (sum >= s) return true;
}
return false;
}
}
④ 首先计算累计和,然后采用二分搜索进行查找。
class Solution { //11ms
public int minSubArrayLen(int s, int[] nums) {
int sum = 0;
int min = Integer.MAX_VALUE;
for (int i = 1;i < nums.length;i ++){
nums[i] += nums[i - 1];
}
for (int i = 0;i < nums.length;i ++){
int j = findWindowEnd(i,nums,s);
if (j == nums.length) break;
min = Math.min(j - i + 1,min);
}
return min == Integer.MAX_VALUE ? 0 : min;
}
public int findWindowEnd(int start,int[] nums,int s){
int i = start;
int j = nums.length - 1;
int offset = start == 0 ? 0 : nums[start - 1];
while(i <= j){
int mid = (i + j) / 2;
int sum = nums[mid] - offset;
if (sum >= s){
j = mid - 1;
}else{
i = mid + 1;
}
}
return i;
}
}