滑动窗口算法中windowStart自增时机及代码等价性疑问
最长连续1滑动窗口代码逻辑解析
原代码
public int longestOnes(int[] nums, int k) { int windowStart = 0, windowEnd; for (windowEnd = 0; windowEnd < nums.length; ++windowEnd) { if (nums[windowEnd] == 0) k--; if (k < 0 && nums[windowStart++] == 0) k++; } return windowEnd - windowStart; }
原条件执行逻辑
if (k < 0 && nums[windowStart++] == 0) k++;的执行规则:
- 逻辑与
&&是短路求值:只有当k < 0为真时,才会执行后半段的判断和操作 - 当
k < 0时,先获取nums[windowStart]的值,立刻将windowStart自增1;如果这个值是0,就把k加1(相当于回收一次翻转0的名额) - 如果
k >= 0,后半段代码完全不会执行,windowStart保持不变
简单说:只要窗口内的0数量超过k(k<0),就必须移动窗口左边界;如果移出的元素是0,就把可用的翻转名额加回来。
两段代码的核心差异
改写后的代码:
public int longestOnes(int[] nums, int k) { int windowStart = 0, windowEnd; for (windowEnd = 0; windowEnd < nums.length; ++windowEnd) { if (nums[windowEnd] == 0) k--; if (k < 0 && nums[windowStart] == 0) k++; if (k < 0) windowStart++; } return windowEnd - windowStart; }
两者的关键区别在于窗口左边界移动的时机和条件:
- 原代码:只要
k < 0,无论移出的元素是不是0,windowStart都会自增;只有当移出的是0时,才恢复k。这保证了窗口始终是当前能容纳最多1(含k个翻转0)的有效窗口,左边界只会单调右移。 - 改写后的代码:先判断当前左边界元素是不是0,是就恢复k,再看k是否还小于0才移动左边界。这会导致:如果移出的是0,k恢复后可能不再小于0,左边界就不移动——窗口会包含超过k个0,破坏了滑动窗口的有效性,最终计算出的窗口长度是错误的。
举个直观例子:nums = [0,0,1,1], k=1
- 原代码执行到
windowEnd=1时,k变为-1,触发条件:取nums[0](0),windowStart变为1,k恢复为0;最终返回4-1=3(正确,最多翻转1个0,最长连续1是3) - 改写后的代码执行到
windowEnd=1时,k=-1:第一个判断恢复k为0,第三个判断k<0不成立,windowStart保持0;最终返回4-0=4(错误,此时窗口包含2个0,超过了k=1的限制)
内容的提问来源于stack exchange,提问作者Bardo
相关产品推荐
相关产品推荐

