LeetCode 2134. 最少交换次数来组合所有的 1 II

发布于:2025-07-24 ⋅ 阅读:(15) ⋅ 点赞:(0)

题目链接

2134. 最少交换次数来组合所有的 1 II

题目描述

给你一个二进制环形数组 nums,返回在任意位置将数组中的所有 1 聚集在一起需要的最少交换次数。

  • 交换:选中数组中两个不同位置并交换二者的值。
  • 环形数组:第一个元素和最后一个元素相邻。

解法分析:滑动窗口(环形处理)

核心思路

该解法通过滑动窗口技术处理环形数组,核心逻辑如下:

  1. 窗口大小确定:所有 1 聚集在一起时,需要一个长度为 ww 是数组中 1 的总数)的窗口(因为最终所有 1 会占据连续 w 个位置)。
  2. 交换次数的本质:窗口中 0 的数量即为需要的交换次数(每个 0 需被窗口外的 1 替换,一次交换可解决一个 0 和一个 1 的位置)。
  3. 环形窗口处理:通过将窗口初始化为数组末尾的 w 个元素,再滑动窗口覆盖所有可能的连续 w 个位置(包括跨首尾的环形情况),找到窗口中 0 数量最少的情况,即为最少交换次数。

代码实现

from typing import List

class Solution:
    def minSwaps(self, nums: List[int]) -> int:
        w = sum(nums)  # 1的总数量,即窗口大小
        n = len(nums)
        if w == 0 or w == n:  # 特殊情况:无1或全是1,无需交换
            return 0
        
        # 初始化左指针:指向窗口的最左侧(环形处理,初始窗口为末尾w个元素)
        left = -w
        # 计算初始窗口中1的数量:窗口为[nums[left], nums[left+1], ..., nums[-1]]
        s = sum(nums[i] for i in range(left, 0))
        
        # 初始窗口的交换次数 = 窗口大小 - 窗口中1的数量(即窗口中0的数量)
        ans = w - s
        
        # 滑动窗口遍历所有可能的位置(共n种可能,循环n-1次完成所有滑动)
        for i in range(n - 1):
            # 窗口右移:加入当前i位置的元素,移除left位置的元素
            s += nums[i] - nums[left]
            # 更新最少交换次数(当前窗口0的数量)
            ans = min(ans, w - s)
            # 左指针右移,保持窗口大小为w
            left += 1
        
        return ans

代码解析

  1. 特殊情况处理

    • w = 0(无 1)或 w = n(全是 1),无需交换,直接返回 0
  2. 窗口初始化

    • w = sum(nums):计算数组中 1 的总数,确定窗口大小为 w(所有 1 聚集后的长度)。
    • left = -w:初始窗口的左边界(负数索引表示从数组末尾开始,如 left = -3 对应数组倒数第3个元素)。
    • s = sum(nums[i] for i in range(left, 0)):计算初始窗口中 1 的数量(窗口为 [left, -1],即数组末尾的 w 个元素)。
  3. 初始交换次数

    • ans = w - s:窗口中 0 的数量即为需要的交换次数(每个 0 需被窗口外的 1 替换)。
  4. 滑动窗口遍历

    • 循环 n-1 次(覆盖所有可能的窗口位置):
      • s += nums[i] - nums[left]:窗口右移,加入当前位置 i 的元素,移除左边界 left 的元素,更新窗口中 1 的数量。
      • ans = min(ans, w - s):更新最少交换次数(当前窗口 0 的数量)。
      • left += 1:左边界右移,保持窗口大小为 w
  5. 返回结果

    • 遍历结束后,ans 即为所有可能窗口中最少的交换次数。

关键逻辑说明

  • 环形数组处理:通过负数索引初始化窗口(指向数组末尾),再通过滑动覆盖所有可能的连续 w 个位置(包括跨首尾的情况,如窗口从末尾延伸到开头)。
  • 交换次数计算:窗口中 0 的数量 = 窗口大小 w - 窗口中 1 的数量 s,这部分 0 必须被窗口外的 1 替换,因此交换次数等于 0 的数量。
  • 窗口滑动效率:每个元素仅被加入和移除窗口各一次,时间复杂度为 O(n),适用于大规模数组。

示例详解

示例1 为例:nums = [0,1,0,1,1,0,0]

  • w = sum(nums) = 3(共3个 1,窗口大小为3)。
  • n = 7left = -3,初始窗口为 range(-3, 0) 即索引 -3, -2, -1(对应数组实际索引 4,5,6)。
  • 初始窗口元素:nums[4]=1, nums[5]=0, nums[6]=0s = 1 + 0 + 0 = 1,初始交换次数 ans = 3 - 1 = 2
循环i 窗口更新(s) 当前交换次数(w-s) ans
i=0 s += nums[0] - nums[-3] → 1 + 0 - 1 = 0 3 - 0 = 3 2
i=1 s += nums[1] - nums[-2] → 0 + 1 - 0 = 1 3 - 1 = 2 2
i=2 s += nums[2] - nums[-1] → 1 + 0 - 0 = 1 3 - 1 = 2 2
i=3 s += nums[3] - nums[0] → 1 + 1 - 0 = 2 3 - 2 = 1 1
i=4 s += nums[4] - nums[1] → 2 + 1 - 1 = 2 3 - 2 = 1 1
i=5 s += nums[5] - nums[2] → 2 + 0 - 0 = 2 3 - 2 = 1 1

最终 ans = 1,与示例输出一致。

复杂度分析

  • 时间复杂度O(n),其中 n 是数组长度。初始化窗口和滑动窗口均为线性遍历,每个元素操作 O(1)
  • 空间复杂度O(1),仅使用常数级额外空间。

总结

该解法通过滑动窗口高效处理环形数组,核心优势在于:

  1. 环形覆盖:通过负数索引初始化窗口,结合滑动遍历所有可能的连续 w 个位置,完美处理环形特性。
  2. 交换次数转化:将问题转化为求窗口中 0 的最小数量,逻辑清晰且计算高效。
  3. 线性效率O(n) 时间复杂度,适用于 n10^5 的大规模输入。

这是处理“环形数组中聚集元素”问题的经典方法,滑动窗口结合环形索引处理的思路可推广到类似场景(如环形数组中聚集 0 或其他元素)。


网站公告

今日签到

点亮在社区的每一天
去签到