LeetCode 1493.删掉一个元素以后全为1的最长子数组

2 阅读4分钟

题目链接

leetcode.cn/problems/lo…

题目描述

给你一个二进制数组 nums,你需要从中删掉一个元素。请你在删掉元素的结果数组中,返回最长的且只包含 1 的非空子数组的长度。如果不存在这样的子数组,请返回 0 。

示例

  • 输入:nums = [1,1,0,1]
    • 输出:3
    • 解释:删掉位置 2 的0后,得到 [1,1,1],包含 3 个 1。
  • 输入:nums = [0,1,1,1,0,1,1,0,1]
    • 输出:5
    • 解释:删掉位置 4 的0后,最长全 1 子数组长度为 5。
  • 输入:nums = [1,1,1]
    • 输出:2
    • 解释:必须删除一个元素,最长全 1 子数组长度为 2。

解法分析:滑动窗口法

核心思路

本题采用滑动窗口(双指针)技术解决,核心思想是维护一个窗口,使得窗口内最多包含1个0。通过移动窗口,找到满足条件的最大窗口长度,该长度即为删除一个0后所能得到的最长全1子数组的长度。

原始代码实现

class Solution:
    def longestSubarray(self, nums: List[int]) -> int:
        l = zeros = ans = 0
        for k, v in enumerate(nums):
            zeros += v ^ 1  # 计算当前元素是否为0,1表示是0,0表示是1
            while zeros > 1:
                zeros -= nums[l] ^ 1  # 减少左指针位置元素的0计数
                l += 1  # 左指针右移
            ans = max(ans, k - l)  # 更新最长窗口长度
        return ans

代码解析

  1. 初始化变量
    • l:滑动窗口的左指针,初始化为0
    • zeros:记录窗口内0的数量,初始化为0
    • ans:记录最长全1子数组的长度,初始化为0
  1. 遍历数组
    • 使用enumerate(nums)同时获取元素索引k和值v
    • zeros += v ^ 1:利用位运算计算当前元素是否为0。因为1 ^ 1 = 0(非0),0 ^ 1 = 1(是0),所以这行代码等价于统计窗口内0的数量
  1. 调整窗口
    • zeros > 1时,说明窗口内0的数量超过1个,需要移动左指针l
    • zeros -= nums[l] ^ 1:减少左指针位置元素的0计数,同样使用位运算判断元素是否为0
    • l += 1:左指针右移,缩小窗口范围,直到窗口内0的数量≤1
  1. 更新最长长度
    • ans = max(ans, k - l):计算当前窗口长度(窗口内最多包含1个0),删除该0后,窗口内所有元素都是1,因此窗口长度即为所求的最长全1子数组长度
    • 最终返回ans

关键技巧说明

  1. 位运算统计0的数量:使用v ^ 1代替条件判断,简洁高效。当v为0时,v ^ 1 = 1,表示增加一个0;当v为1时,v ^ 1 = 0,不影响0的计数。
  2. 滑动窗口维护
    • 右指针不断向右扩展,扩大窗口范围
    • 当窗口内0的数量超过1时,左指针向右移动,缩小窗口范围
    • 确保窗口内最多包含1个0,这样删除该0后,窗口内所有元素都是1
  1. 窗口长度计算:窗口长度为k - l,其中k是右指针索引,l是左指针索引。这个长度即为删除一个0后全为1的子数组长度。

复杂度分析

  • 时间复杂度:O(n),其中n是数组的长度。左右指针各遍历数组一次,每个元素最多被访问两次。
  • 空间复杂度:O(1),只使用了常数级别的额外空间。

示例详解

以输入nums = [1,1,0,1]为例:

  1. 初始状态:l=0, zeros=0, ans=0
  2. k=0, v=1
    • zeros += 1 ^ 1 = 0zeros=0
    • zeros <= 1,不进入循环
    • ans = max(0, 0-0)=0
  1. k=1, v=1
    • zeros += 1 ^ 1 = 0zeros=0
    • zeros <= 1,不进入循环
    • ans = max(0, 1-0)=1
  1. k=2, v=0
    • zeros += 0 ^ 1 = 1zeros=1
    • zeros <= 1,不进入循环
    • ans = max(1, 2-0)=2
  1. k=3, v=1
    • zeros += 1 ^ 1 = 0zeros=1
    • zeros <= 1,不进入循环
    • ans = max(2, 3-0)=3
  1. 最终返回ans=3,符合示例1的输出。

总结

该解法利用滑动窗口技术高效地解决了问题,通过维护窗口内0的数量不超过1个,确保删除一个0后窗口内全为1。算法使用位运算优化了0的计数过程,时间复杂度为O(n),空间复杂度为O(1),是解决该问题的最优解法之一。这种滑动窗口控制特定元素数量的思路,可广泛应用于类似的子数组问题。