利用滑动窗口与位运算解决“最长交替子数组”问题 —— 面试必备技巧解析
在日常的算法练习和面试题中,数组处理是最为常见的题型之一。今天我们来聊一道非常有代表性的题目,它考察了我们对滑动窗口、条件判断逻辑,甚至位运算优化的综合掌握。
🧩 题目描述
给你一个下标从 0 开始的整数数组 nums
和一个整数 threshold
。
请你从 nums
的子数组中找出以下标 l
开头、下标 r
结尾(0 <= l <= r < nums.length)且满足以下条件的最长子数组:
nums[l] % 2 == 0
:子数组必须从一个偶数开始;- 对于范围
[l, r - 1]
内的所有下标 i ,nums[i] % 2 != nums[i + 1] % 2
:数组元素的奇偶性必须交替变化; - 对于范围
[l, r]
内的所有下标 i ,nums[i] <= threshold
:所有元素不得超过阈值。
你的任务是返回满足条件的最长子数组的长度。
🧠 解题思路
为了满足题目的三个条件,我们需要一种既能快速判断当前子数组是否合法,又能高效遍历所有可能起点的方法。答案是——滑动窗口(双指针)+ 条件剪枝。
滑动窗口模型适用于:
- 连续子数组问题
- 带有前后元素依赖关系的问题(如奇偶交替)
🚀 解法一:标准滑动窗口 + 条件判断
我们先来看一个清晰直观的滑动窗口解法。
✅ 步骤如下:
- 从头遍历数组;
- 遇到偶数且小于等于
threshold
的元素时,作为一个合法起点; - 然后从该点向右扩展,要求每个新元素必须:
-
- 不超过
threshold
; - 与上一个数奇偶性不同;
- 不超过
- 每次更新当前最长的子数组长度。
💡 Python代码如下:
def longestAlternatingSubarray(nums, threshold):
max_len = 0
i = 0
n = len(nums)
while i < n:
if nums[i] % 2 == 0 and nums[i] <= threshold:
j = i
while j < n and nums[j] <= threshold and (j == i or nums[j] % 2 != nums[j - 1] % 2):
j += 1
max_len = max(max_len, j - i)
i += 1
return max_len
⚡ 解法二:进阶优化 —— 使用位运算与剪枝
我们可以通过位运算来进一步优化判断奇偶交替的过程,使代码更简洁、运行更快。
🌟 关键技巧:
x & 1
:判断奇偶性,奇数为 1,偶数为 0;a ^ b
:异或操作,判断两数奇偶是否不同;
-
- 同为奇/偶 ⇒ 异或为 0;
- 一奇一偶 ⇒ 异或为 1。
🔥 最优解代码:
class Solution:
def longestAlternatingSubarray(self, nums: List[int], threshold: int) -> int:
i = ans = 0
n = len(nums)
while i < n:
if nums[i] > threshold or nums[i] & 1:
i += 1
continue
start = i
i += 1
while i < n and nums[i] <= threshold and (nums[i] & 1) ^ (nums[i - 1] & 1):
i += 1
ans = max(ans, i - start)
return ans
✅ 测试示例
nums = [3, 2, 5, 4, 7, 6, 5]
threshold = 8
# 有效子数组:[2, 5, 4, 7, 6, 5] 长度为 6
print(Solution().longestAlternatingSubarray(nums, threshold)) # 输出 6
🧮 时间与空间复杂度分析
- 时间复杂度:O(n)
每个元素最多被访问两次(一次作为起点,一次作为扩展点)。 - 空间复杂度:O(1)
无额外空间开销,仅使用常量级变量。
🏁 总结
这道题的关键在于:
- 识别滑动窗口应用场景;
- 灵活运用位运算判断奇偶交替;
- 合理剪枝,避免无意义遍历。
掌握这样的题目,不仅可以提升写代码的技巧,更能在面试中脱颖而出。强烈建议大家多多练习这类“多条件滑动窗口类问题”,会对你构建编程直觉有非常大帮助。
如果你喜欢这类题解风格,欢迎点赞收藏交流~