执行结果:通过
执行用时和内存消耗如下:
int minimumSubarrayLength(int* nums, int numsSize, int k) {
int left = 0;
int right = 0;
int currentOr = 0;
int minLength = INT_MAX;
while (right < numsSize) {
// 扩展窗口,添加新元素到 currentOr
currentOr |= nums[right];
// 当 currentOr >= k 时,尝试收缩窗口
while (currentOr >= k && left <= right) {
// 更新最短长度
if (right - left + 1 < minLength) {
minLength = right - left + 1;
}
// 移除左端点元素,并更新 currentOr
int tempOr = 0;
for (int i = left + 1; i <= right; ++i) {
tempOr |= nums[i];
}
currentOr = tempOr;
++left;
}
++right;
}
// 检查是否有符合条件的子数组
return minLength == INT_MAX ? -1 : minLength;
}
解题思路:
这段代码的解题思路旨在寻找一个最短的连续子数组,使得该子数组中所有数字的按位或(bitwise OR)结果大于等于给定的整数 k
。以下是详细的解题步骤:
- 初始化变量 :
left
和right
分别表示当前考虑的子数组的左右边界,初始都设为 0。currentOr
用于存储当前子数组内所有元素的按位或结果,初始化为 0。minLength
用于记录符合条件的最短子数组的长度,初始化为INT_MAX
(一个非常大的数,表示初始时未找到符合条件的子数组)。
- 遍历数组 :
- 使用一个
while
循环遍历数组,条件是right < numsSize
,即右边界未超出数组长度。
- 使用一个
- 扩展窗口 :
- 在每次循环中,首先通过
currentOr |= nums[right]
将当前元素加入到currentOr
中,扩展窗口的右边界。
- 在每次循环中,首先通过
- 尝试收缩窗口 :
- 检查
currentOr
是否大于等于k
。如果是,说明当前窗口(或更大的窗口)可能包含符合条件的子数组。 - 进入一个内层
while
循环,条件是currentOr >= k
且left <= right
。这个循环尝试收缩窗口的左边界,以找到最短的可能子数组。
- 检查
- 更新最短长度 :
- 在内层循环中,如果当前窗口的长度(
right - left + 1
)小于minLength
,则更新minLength
。
- 在内层循环中,如果当前窗口的长度(
- 收缩窗口并更新
currentOr
:- 为了收缩窗口,需要从当前窗口中移除左边界的元素。代码中的实现方式是通过一个循环从
left + 1
到right
重新计算currentOr
(这里有一个效率问题,下面会提到)。 - 更新
currentOr
为不包含左边界元素的新值。 - 将
left
右移一位,继续尝试收缩窗口。
- 为了收缩窗口,需要从当前窗口中移除左边界的元素。代码中的实现方式是通过一个循环从
- 继续扩展窗口 :
- 完成内层循环后,继续扩展外层循环的右边界,即将
right
右移一位。
- 完成内层循环后,继续扩展外层循环的右边界,即将
- 返回结果 :
- 循环结束后,检查
minLength
是否仍为INT_MAX
。如果是,说明没有找到符合条件的子数组,返回-1
。 - 否则,返回
minLength
,即符合条件的最短子数组的长度。
- 循环结束后,检查
效率问题:
- 在收缩窗口的过程中,代码通过一个循环重新计算
currentOr
,这实际上是不必要的。可以通过记录移除元素之前的currentOr
值和移除元素本身,然后使用按位运算直接更新currentOr
,从而提高效率。
优化思路:
- 可以使用一个变量来跟踪上一次计算
currentOr
时左边界元素对currentOr
的贡献,这样在移除左边界元素时,可以直接通过按位运算减去该贡献,而无需重新计算整个窗口的currentOr
。