如何解决算法:我怎么不能对这个问题使用滑动窗口方法?
我在面试时遇到了这个问题。它为您提供一个数组和一个阈值,该函数应返回该数组的最短、非空、连续子数组的长度,其总和至少超过该阈值。
所以如果数组是 [2,-1,2]
并且阈值是 3
那么它应该返回 3
。
这是我用 JavaScript 编写的尝试。我采用了典型的滑动窗口方法,一旦总和大于 threshold
,我将减小窗口大小,并在每次迭代期间跟踪窗口大小。
function(array,threshold) {
let minWindowSize = Infinity
let sum = 0
for (let start = 0,end = 0; end < array.length; end++) {
sum += array[end]
if(sum >= threshold) minWindowSize = Math.min(minWindowSize,end - start + 1)
while (sum > threshold) {
sum -= array[start++]
}
}
return minWindowSize === Infinity ? -1 : minWindowSize
};
但是我的解决方案对于像这样的情况有问题
array = [17,85,93,-45,-21]
threshold = 150
我想知道这个问题与典型的滑动窗口问题有什么区别,有没有办法实现滑动窗口方法来解决这个问题?看起来很简单,但结果是 be a hard question on leetcode。
解决方法
正如 David 所指出的,当数组中有负数时,您不能使用滑动/拉伸窗口技术,因为总和不会随着窗口大小而单调增长。
你仍然可以在 O(n log n) 时间内解决这个问题,使用一种通常用于“滑动窗口最小值/最大值”问题的技术。
首先,将您的数组转换为前缀 sum 数组,将每个元素替换为该元素和所有先前元素的总和。现在你的问题变成了“找到最接近的一对差异 >= X 的元素”(假设 array[-1]==0)。
当您遍历数组时,您需要为每个 i 找到最新的索引 j,使得 j 和array[j] 。
为了快速做到这一点,首先要注意 array[j] 将总是小于以下所有元素,直到 i ,因为否则会有更接近的元素可供选择。
因此,当您遍历数组时,请维护一个所有元素的索引堆栈,这些元素的索引小于您看到的所有后续元素。这很简单,总体上需要 O(n) 时间——在处理每个 i 之后,您只需弹出所有具有 >= 值的索引,然后推送 i。 >
然后对于每个i,你可以在这个栈中做一个二分查找,找到一个值足够小的最新索引。二分查找有效,因为堆栈中 index 的值是单调增加的——每个元素必须小于以下所有元素。
使用二分查找,总时间增加到 O(n log n)。
在 JavaScript 中,它看起来像这样:
var shortestSubarray = function(A,K) {
//transform to prefix sum array
let sum=0;
const sums = A.map(val => {
sum+=val;
return sum;
});
const stack=[];
let bestlen = -1;
for(let i=0; i<A.length; ++i) {
const targetVal = sums[i]-K;
//binary search to find the shortest acceptable span
//we will find the position in the stack *after* the
//target value
let minpos=0;
let maxpos=stack.length;
while(minpos < maxpos) {
const testpos = Math.floor(minpos + (maxpos-minpos)/2);
if (sums[stack[testpos]]<=targetVal) {
//value is acceptable.
minpos=testpos+1;
} else {
//need a smaller value - go left
maxpos=testpos;
}
}
if (minpos > 0) {
//found a span
const spanlen = i - stack[minpos-1];
if (bestlen < 0 || spanlen < bestlen) {
bestlen = spanlen;
}
} else if (bestlen < 0 && targetVal>=0) {
// the whole prefix is a valid span
bestlen = i+1;
}
// Add i to the stack
while(stack.length && sums[stack[stack.length-1]] >= sums[i]) {
stack.pop();
}
stack.push(i);
}
return bestlen;
};
Leetcode 说:
成功:
运行时间:216 毫秒,比 100.00% 的 JavaScript 在线提交快 求和至少为 K 的最短子数组。
内存占用:50.1 MB,不到 JavaScript 在线的 37.37% 提交总和至少为 K 的最短子阵列。
我猜大多数人使用了较慢的算法。
,如果所有数组元素都是非负的,您可以使用滑动窗口。问题是,对于负元素,一个子数组可能比另一个子数组更短,并且比另一个子数组的和更大。
不知道怎么用滑动窗口解决这个问题。我想到的方法是遍历前缀总和,在搜索段树以查找至少 <div class="profile-buttons" id = "profile-buttons">
<a id = "btn_articles" href = "#" class = "btn-profile-buttons active">Articles </a>
<a id = "btn_posts" href = "#" class = "btn-profile-buttons ">Posts </a>
</div>
小的最近总和后,将每个插入到段树中。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。