一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第12天,点击查看活动详情。
说明:文章部分内容及图片出自网络,如有侵权请与我本人联系(主页有公众号:小攻城狮学前端)
作者:小只前端攻城狮、 主页:小只前端攻城狮的主页、 来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
【LeetCode 1658. 将 x 减到 0 的最小操作数 】- JavaScript(滑动窗口+前缀和+二分)
题意描述
给你一个整数数组 nums 和一个整数 x 。每一次操作时,你应当移除数组 nums 最左边或最右边的元素,然后从 x 中减去该元素的值。请注意,需要 修改 数组以供接下来的操作使用。
如果可以将 x 恰好 减到 0 ,返回 最小操作数 ;否则,返回 -1 。
示例 1:
输入:nums = [1,1,4,2,3], x = 5 输出:2 解释:最佳解决方案是移除后两个元素,将 x 减到 0 。
示例 2:
输入:nums = [3,2,20,1,1,3], x = 10 输出:5 解释:最佳解决方案是移除后三个元素和前两个元素(总共 5 次操作),将 x 减到 0 。
滑动窗口
因为数组两边都可以拿走数字,要使得最后剩下的是连续的最长子数组,用滑动窗口解决,窗口的固定和就是数组总和 - x,要求最长的窗口长度。最后数组长度 - 最长窗口长度。
注意:先看一下right的下一位是不是符合要求,再决定滑不滑动right到下一位 先滑动right到下一位,在看看滑动后的right是不是符合要求
function minOperations(nums, x) {
let sum = 0
for (let i = 0; i < nums.length; i++) {
sum += nums[i]
}
const windowSumTarget = sum - x
if (windowSumTarget < 0) {
return -1
}
let left = 0
let right = 0
let operateCount = Number.MAX_VALUE
let windowSum = 0
while (right < nums.length) {
windowSum += nums[right]
while (windowSum > windowSumTarget) {
windowSum -= nums[left]
left++
}
if (windowSum === windowSumTarget) {
operateCount = Math.min(operateCount, nums.length - (right - left + 1))
}
right++
}
return operateCount === Number.MAX_VALUE ? -1 : operateCount
}
前缀和+二分
思路:
我们可以注意到
nums中每个元素均为正数,那么这个的前缀和数组一定是(严格)单调递增的,我们可以利用这一特点,来枚举子数组的一个端点,然后二分查找另一个端点。一直到家~注意的是针对左右区间,可以推导
total-(sum[i]-sum[j])=x ==> sum[j]=sum[i]-total+x
元素>=1。即表示sum_j>0,则left存在操作,j+1(j为索引从0开始算需+1);如sum_j=0则表示左边没有操作数j
- 左操作数区间和+有操作数区间和=x,即sum[j]+[total-(sum[i]-sum[j])-sum[j]]=x ,即sum[j]=sum[i]-total+x
- 作数数量:左=j+1或j,右=length-(i+1)。操作数量=左+右,注:当sum[j]=0则不存在操作数
var minOperations = function(nums, x) {
let presum = [0];
nums.forEach((value)=>{
presum.push(presum[presum.length -1]+value);
})
let res = nums.length, n = nums.length, sum = presum[n];
if (sum === x) return n;
for(let i = 0; i < n ; i++){
let r_left = i, r_right = n - 1;
while(r_left <= r_right){
let r_mid = r_left + ((r_right - r_left ) >> 1);
if(sum - (presum[r_mid + 1] - presum[i]) < x){
r_right = r_mid - 1;
} else {
r_left = r_mid + 1;
}
}
if(sum - (presum[r_right + 1] - presum[i]) === x) {
res = Math.min(res, n - (r_right - i +1));
}
}
return res===nums.length?-1:res;
};
感谢阅读,希望能对你有所帮助,文章若有错误或者侵权,可以在评论区留言或在我的主页添加公众号联系我。
写作不易,如果觉得不错,可以「点赞」+「评论」 谢谢支持❤