LeetCode 2134 - Minimum Swaps to Group All 1's Together II
The problem is asking us to find the minimum number of swaps required to group all 1s in a binary circular array together at any contiguous segment. The array is circular, which means the end of the array wraps around to the beginning.
Difficulty: 🟡 Medium
Topics: Array, Sliding Window
Solution
Problem Understanding
The problem is asking us to find the minimum number of swaps required to group all 1s in a binary circular array together at any contiguous segment. The array is circular, which means the end of the array wraps around to the beginning. Each swap can only exchange the values of two distinct positions in the array. The input is a binary array, meaning every element is either 0 or 1. The expected output is a single integer representing the minimum number of swaps needed.
Constraints tell us that the array can be large, up to 10^5 elements, which makes a naive approach that checks every possible grouping infeasible. We also know that the array may already have all 1s grouped or may require the circular property to minimize swaps. Important edge cases include arrays where all elements are 1s or 0s, arrays with alternating 0s and 1s, and arrays where the minimal grouping occurs across the boundary between the last and first element.
Approaches
Brute Force
A brute-force approach would consider every possible contiguous subarray of length equal to the total number of 1s in the array and calculate how many 0s are in that segment. Each 0 represents a needed swap to bring a 1 into the segment. Because the array is circular, we would need to consider segments that wrap around from the end to the beginning. This approach works correctly but is inefficient, because for an array of length n and k ones, we would examine n subarrays and count 0s in each subarray in O(k) time, resulting in O(n*k) time complexity, which is too slow for large arrays.
Optimal Approach
The key insight is that the minimum number of swaps is equal to the minimum number of 0s in any contiguous subarray of length equal to the total number of 1s. To handle the circular property efficiently, we can duplicate the array (concatenate it to itself) and apply a sliding window of length equal to the number of 1s. This way, every circular segment is represented as a standard linear segment in the doubled array. Using a sliding window, we can maintain a running count of 0s and efficiently determine the minimum count across all windows.
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(n*k) | O(1) | Examine all subarrays of length k and count 0s |
| Optimal | O(n) | O(n) | Duplicate array and use sliding window to track 0s |
Algorithm Walkthrough
- Count the total number of
1s in the array and store it asnumOnes. - If
numOnesis0or equal to the array length, return0immediately because no swaps are needed. - Duplicate the array by concatenating it to itself, creating
numsExtended. - Initialize a sliding window of length
numOnesand count the number of0s in the first window. Store this count asminZeros. - Slide the window across
numsExtendedfrom index1tolen(nums). For each step, remove the effect of the element leaving the window and add the effect of the element entering the window:
- If the outgoing element is
0, decrease the zero count. - If the incoming element is
0, increase the zero count. - Update
minZerosif the current zero count is smaller.
- After sliding through all possible windows,
minZeroswill represent the minimum number of swaps needed. - Return
minZeros.
Why it works: The algorithm relies on the invariant that the minimum number of 0s in any window of size equal to the total number of 1s corresponds exactly to the minimum swaps required. Duplicating the array allows us to handle circular subarrays as linear ones without special cases.
Python Solution
from typing import List
class Solution:
def minSwaps(self, nums: List[int]) -> int:
numOnes = sum(nums)
if numOnes <= 1:
return 0
n = len(nums)
numsExtended = nums + nums
currentZeros = numsExtended[:numOnes].count(0)
minZeros = currentZeros
for i in range(1, n):
if numsExtended[i - 1] == 0:
currentZeros -= 1
if numsExtended[i + numOnes - 1] == 0:
currentZeros += 1
minZeros = min(minZeros, currentZeros)
return minZeros
The implementation first counts all 1s to determine the window size. By extending the array, we can apply a sliding window without special circular logic. The zero count in each window directly maps to the swaps required to group all 1s, and we track the minimum across all possible windows.
Go Solution
func minSwaps(nums []int) int {
numOnes := 0
for _, v := range nums {
if v == 1 {
numOnes++
}
}
if numOnes <= 1 {
return 0
}
n := len(nums)
numsExtended := append(nums, nums...)
currentZeros := 0
for i := 0; i < numOnes; i++ {
if numsExtended[i] == 0 {
currentZeros++
}
}
minZeros := currentZeros
for i := 1; i < n; i++ {
if numsExtended[i-1] == 0 {
currentZeros--
}
if numsExtended[i+numOnes-1] == 0 {
currentZeros++
}
if currentZeros < minZeros {
minZeros = currentZeros
}
}
return minZeros
}
The Go solution follows the same logic. Go requires explicit handling of slices and does not have a built-in count function, so we count zeros manually. The sliding window logic remains identical.
Worked Examples
Example 1: [0,1,0,1,1,0,0]
numOnes = 3, window size = 3.
| Window start | Window elements | Zero count | MinZeros |
|---|---|---|---|
| 0 | [0,1,0] | 2 | 2 |
| 1 | [1,0,1] | 1 | 1 |
| 2 | [0,1,1] | 1 | 1 |
| 3 | [1,1,0] | 1 | 1 |
| 4 | [1,0,0] | 2 | 1 |
| 5 | [0,0,0] | 3 | 1 |
| 6 | [0,0,1] | 2 | 1 |
Output: 1
Example 2: [0,1,1,1,0,0,1,1,0]
numOnes = 5, window size = 5.
| Window start | Window elements | Zero count | MinZeros |
|---|---|---|---|
| 0 | [0,1,1,1,0] | 2 | 2 |
| 1 | [1,1,1,0,0] | 2 | 2 |
| 2 | [1,1,0,0,1] | 2 | 2 |
| 3 | [1,0,0,1,1] | 2 | 2 |
| 4 | [0,0,1,1,0] | 3 | 2 |
| 5 | [0,1,1,0,0] | 3 | 2 |
| 6 | [1,1,0,0,0] | 3 | 2 |
| 7 | [1,0,0,0,1] | 3 | 2 |
| 8 | [0,0,0,1,1] | 3 | 2 |
Output: 2
Example 3: [1,1,0,0,1]
numOnes = 3, window size = 3.
| Window start | Window elements | Zero count | MinZeros |
|---|---|---|---|
| 0 | [1,1,0] | 1 | 1 |
| 1 | [1,0,0] | 2 | 1 |
| 2 | [0,0,1] | 2 | 1 |
| 3 | [0,1,1] | 1 | 1 |
| 4 | [1,1,1] | 0 | 0 |
Output: 0
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(n) | Counting ones and sliding window each iterate over at most 2n elements |
| Space | O(n) | Doubling the array for circular handling |
The time complexity is linear because each element is processed a constant number of times. The space complexity is linear due to the duplicated array.
Test Cases
# provided examples
assert Solution().minSwaps([0,1,0,1,1,0,0]) == 1
assert Solution().minSwaps([0,1,1,