题目
力扣链接 长度最小子数组
给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。
示例:
输入:s = 7, nums = [2,3,1,2,4,3] 输出:2 解释:子数组 [4,3] 是该条件下的长度最小的子数组。
提示:
- 1 <= target <= 10^9
- 1 <= nums.length <= 10^5
- 1 <= nums[i] <= 10^5
解法
暴力解法
首先这是一个数组的遍历问题,我们一开始就能想到暴力解法就是直接通过双重for循环
- 先遍历每一个数字
- 再遍历后面到结尾的数字进行相加
- 假如相加的结果大于
s就直接通过break退出循环 - 记录已经遍历的长度保存
- 对比多个遍历的长度,记录最短的长度返回
/**
* @param {number} target
* @param {number[]} nums
* @return {number}
*/
var minSubArrayLen = function(target, nums) {
let x = Infinity; //对比最小的话初始化需要最大
for (let i = 0; i < nums.length; i++) { //外层遍历,遍历每一个数字
let sum = nums[i]; //记录数字的第一个元素
if (sum >= target) { //假如第一个数字就已经满足目标值,直接赋值后退出循环
x = 1;
break;
}
for (let j = i + 1; j <= nums.length - 1; j++) { //内部循环,从i的下一个开始,到数组末尾结束
sum += nums[j]; //累加
if (sum >= target) { //假如sum大于等于目标值
let res = j - i + 1; //记录当前数组长度
x = res < x ? res : x //假如新长度更小,进行赋值,否则不改动
break; //找到后跳出内部循环
}
}
}
return x===Infinity?0:x //假如x未被赋值过,则返回0
};
因为用到了双重for循环,所以时间复杂度是O(n^2)。
双指针滑动窗口
使用暴力解法的话需要遍历很多次,假如不用break及时退出循环,在力扣是会超时的。虽然能解决问题,但是明显效率是不高的。
那有没有一种方法可以只遍历一次就计算出最短长度呢?
所谓滑动窗口,就是不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果。
双指针的思路具体如下:
- 首先需要一个for循环遍历数据
- 将循环索引
j当成窗口的末尾,定义一个i当成窗口的起始位置 - 计算
j累加的和,当大于目标值时,记录长度 - 同时累加值减去
i所对应的值,i向后移一位,假如仍然大于目标值,则继续记录长度和向后移 - 最后记录最短长度
参考动图
动图来自代码随想录
var minSubArrayLen = function(target, nums) {
let res = Infinity //定义结果,大小为无限大
let sum = 0,i=0,slength =0; //定义计算结果sum,起始位置i,数组长度slength
for(let j=0;j<nums.length;j++){ //外层遍历,j为末尾位置
sum+=nums[j] //计算j累加的值
while(sum>=target){ //这里while是因为假如i向后移一位仍然大于目标值,则需要不断后移知道小于目标值
slength= j-i+1 //记录长度
sum-=nums[i] //计算结果减去i对应的值
i++; //i向后移
res = res<slength?res:slength //对比哪个更小
}
}
return res===Infinity?0:res //假如未被赋值过,则返回0
};
总结
数组问题,感觉大部分都可以用双指针来达到更好的效果,但是也有可能我刚入门学的不深。
如果有更好的解法欢迎交流。