LeetCode 第3097题:按位或值至少为 K 的最短子数组 II
LeetCode 第3097题:按位或值至少为 K 的最短子数组 II
在本文中,我们将深入探讨 LeetCode 第3097题:按位或值至少为 K 的最短子数组 II。我们将详细解析题目描述,理解约束条件,并逐步讲解一个高效的 Python 解法,帮助你有效解决这一问题。
题目描述
给定一个 非负整数数组 nums
和一个整数 k
,请你找到 nums
中满足以下条件的 最短非空子数组 的长度:
- 子数组中所有元素的 按位或运算(OR) 的值 至少为
k
。
如果不存在这样的子数组,则返回 -1
。
定义
-
按位或(OR):一种二进制运算,对应位上有一个为
1
时结果位为1
,否则为0
。例如:1 | 2 = 3 2 | 1 = 3
-
子数组:数组中连续的一部分元素。
示例
示例 1:
输入:nums = [1, 2, 3], k = 2
输出:1
解释:
子数组 [3] 的按位或值为 3,满足 3 >= 2,所以返回 1。
示例 2:
输入:nums = [2, 1, 8], k = 10
输出:3
解释:
子数组 [2, 1, 8] 的按位或值为 11,满足 11 >= 10,所以返回 3。
示例 3:
输入:nums = [1, 2], k = 0
输出:1
解释:
任何非空子数组的按位或值都至少为 0,所以返回 1。
约束条件
1 <= nums.length <= 2 * 10^5
0 <= nums[i] <= 10^9
0 <= k <= 10^9
解题思路
要高效地解决这个问题,我们可以采用 贪心算法 结合 按位运算 的特性,来优化寻找满足条件的最短子数组的过程。
关键观察点
-
即时满足:如果数组中存在一个元素本身就大于或等于
k
,那么最短的子数组长度为1
。 -
按位或的单调性:随着子数组长度的增加,按位或的结果值要么保持不变,要么增加。这一特性可以帮助我们优化搜索过程。
-
状态追踪:通过维护一个字典,记录每个可能的按位或结果及其对应的最短子数组长度,可以有效减少重复计算。
具体步骤
-
初始化:设置一个变量
ans
为无穷大,用于记录当前找到的最短子数组长度。 -
遍历数组:
- 对于数组中的每一个元素
x
,如果x
本身就大于或等于k
,立即返回1
。 - 否则,尝试将当前元素与之前的元素进行按位或操作,更新之前元素的按位或值。
- 如果某个更新后的按位或值满足条件,则更新
ans
为当前子数组长度。
- 对于数组中的每一个元素
-
结果判断:遍历完成后,如果
ans
仍为无穷大,说明不存在满足条件的子数组,返回-1
。否则,返回ans
。
代码实现
以下是上述思路的 Python 实现:
from typing import List
class Solution:
def minimumSubarrayLength(self, nums: List[int], k: int) -> int:
ans = float('inf') # 初始化答案为无穷大
for i, x in enumerate(nums):
if x >= k:
return 1 # 如果当前元素满足条件,返回1
j = i - 1
while j >= 0 and (nums[j] | x) != nums[j]:
nums[j] |= x # 更新前一个元素的按位或值
if nums[j] >= k:
ans = min(ans, i - j + 1) # 更新最短子数组长度
j -= 1
return ans if ans < float('inf') else -1 # 返回最终结果
代码解析
-
初始化:
ans = float('inf')
- 将
ans
初始化为无穷大,表示尚未找到满足条件的子数组。
- 将
-
遍历数组元素:
for i, x in enumerate(nums):
- 使用
enumerate
遍历数组,同时获取元素的索引i
和值x
。
- 使用
-
单元素检查:
if x >= k: return 1
- 如果当前元素
x
本身就满足x >= k
,则最短子数组长度为1
,直接返回。
- 如果当前元素
-
更新按位或值:
j = i - 1 while j >= 0 and (nums[j] | x) != nums[j]: nums[j] |= x if nums[j] >= k: ans = min(ans, i - j + 1) j -= 1
- 初始化
j
为当前元素的前一个元素索引。 - 使用
while
循环向前遍历,更新前一个元素的按位或值。 - 如果更新后的值满足
nums[j] >= k
,则更新ans
为当前子数组长度与已有最短长度的较小值。 - 循环终止条件为索引越界或按位或操作后值不变,避免不必要的计算。
- 初始化
-
返回结果:
return ans if ans < float('inf') else -1
- 如果
ans
被更新过,返回其值;否则,返回-1
,表示不存在满足条件的子数组。
- 如果
复杂度分析
-
时间复杂度:O(n),其中
n
是数组nums
的长度。- 虽然内层循环看似可能达到 O(n^2),但由于按位或操作的性质,实际上每个元素的按位或值最多变化
30
次(因为nums[i]
最大为10^9
,二进制表示不超过30
位),所以整体时间复杂度为线性。
- 虽然内层循环看似可能达到 O(n^2),但由于按位或操作的性质,实际上每个元素的按位或值最多变化
-
空间复杂度:O(1)。
- 我们在原地修改了输入数组
nums
,并且只使用了常数级别的额外空间。
- 我们在原地修改了输入数组
示例解析
以 示例 1 为例:
输入:nums = [1, 2, 3], k = 2
-
迭代 1 (
i = 0
,x = 1
):1 < 2
,继续。j = -1
(没有前一个元素),跳过。
-
迭代 2 (
i = 1
,x = 2
):2 >= 2
,满足条件,立即返回1
。
因此,函数正确返回 1
。
结论
通过利用按位或操作的单调性和贪心策略,我们能够高效地找到满足条件的最短子数组。这种方法不仅时间复杂度低,而且实现简洁,适用于处理大规模的数据输入。
如果你有任何疑问或更优化的解法,欢迎在评论区交流分享!
原文地址:https://blog.csdn.net/qq_17405059/article/details/145182104
免责声明:本站文章内容转载自网络资源,如侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!