2134. Minimum Swaps to Group All 1's Together II
Problem Description
You are given a binary circular array nums
(containing only 0s and 1s). In a circular array, the first and last elements are considered adjacent to each other.
A swap operation means taking two distinct positions in the array and exchanging their values.
Your task is to find the minimum number of swaps needed to group all the 1s together at any location in the array.
For example, if you have nums = [1,0,1,0,1]
, you could group all 1s together to form [0,0,1,1,1]
or [1,1,1,0,0]
(remember it's circular, so the 1s at the beginning and end are adjacent).
The key insight is that since the array is circular, you need to find the best consecutive segment of length k
(where k
is the total number of 1s) that already contains the most 1s. The minimum swaps needed would be k
minus the number of 1s already in that optimal segment.
The solution uses a sliding window approach to check all possible windows of size k
in the circular array, keeping track of which window contains the maximum number of 1s. The window slides through positions [0, k-1]
, [1, k]
, ..., and wraps around using modulo arithmetic to handle the circular nature of the array.
Intuition
Since we want to group all 1s together, let's think about what the final result looks like. If we have k
ones in total, then in the final configuration, these k
ones will occupy k
consecutive positions in the circular array.
The key realization is that we don't need to determine exactly how to perform the swaps. Instead, we can think about it differently: if we choose any segment of length k
in the circular array to be our "target zone" where all 1s should be grouped, how many 1s are already in that zone?
If a segment already contains m
ones, then there are k - m
ones outside this segment that need to be swapped in. Each of these outside 1s needs exactly one swap with a 0 inside the segment. Therefore, the number of swaps needed is k - m
.
To minimize the number of swaps, we want to maximize m
- find the segment of length k
that already contains the most 1s. This transforms our problem into finding the maximum sum of a sliding window of size k
in a circular binary array.
The circular nature of the array means we need to consider windows that wrap around. For instance, in array [0,1,0,1,1]
with window size 3, we need to check windows like [1,1,0]
(positions 3,4,0). We handle this by extending our iteration to n + k
positions and using modulo arithmetic to wrap around: nums[i % n]
gives us the circular access pattern we need.
This sliding window approach efficiently checks all possible target zones and finds the one requiring the minimum number of swaps.
Learn more about Sliding Window patterns.
Solution Approach
First, we count the total number of 1s in the array using k = nums.count(1)
. This tells us the size of the window we need to consider - all 1s must eventually be grouped into a consecutive segment of length k
.
Next, we initialize our sliding window by calculating the sum of the first k
elements: mx = cnt = sum(nums[:k])
. Here, cnt
represents the current count of 1s in our window, and mx
tracks the maximum count we've seen so far.
Now we slide the window through the circular array. The loop runs from position k
to n + k - 1
, which ensures we check all possible windows in the circular array:
for i in range(k, n + k):
cnt += nums[i % n]
cnt -= nums[(i - k + n) % n]
mx = max(mx, cnt)
For each iteration:
nums[i % n]
adds the new element entering the window (using modulo to handle circular indexing)nums[(i - k + n) % n]
removes the element leaving the window (the one that'sk
positions behind)- We update
mx
to keep track of the maximum number of 1s found in any window
The expression (i - k + n) % n
ensures proper circular indexing for the element being removed. Adding n
before taking modulo handles cases where i - k
might be negative.
Finally, we return k - mx
. Since mx
represents the maximum number of 1s already positioned correctly in some window of size k
, we need k - mx
swaps to bring the remaining 1s into that window.
Time complexity: O(n)
where n
is the length of the array, as we slide through the array once.
Space complexity: O(1)
as we only use a constant amount of extra space.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the solution with nums = [0,1,0,1,1,0,0]
.
Step 1: Count total 1s
- Count all 1s in the array:
k = 3
- This means we need to group all three 1s into a consecutive segment of length 3
Step 2: Initialize the first window
- First window of size 3:
[0,1,0]
(positions 0-2) - Initial count:
cnt = 0 + 1 + 0 = 1
- Maximum so far:
mx = 1
Step 3: Slide the window through the circular array
Window at position [1,2,3]: [1,0,1]
- Add
nums[3] = 1
:cnt = 1 + 1 = 2
- Remove
nums[0] = 0
:cnt = 2 - 0 = 2
- Update
mx = max(1, 2) = 2
Window at position [2,3,4]: [0,1,1]
- Add
nums[4] = 1
:cnt = 2 + 1 = 3
- Remove
nums[1] = 1
:cnt = 3 - 1 = 2
- Update
mx = max(2, 2) = 2
Window at position [3,4,5]: [1,1,0]
- Add
nums[5] = 0
:cnt = 2 + 0 = 2
- Remove
nums[2] = 0
:cnt = 2 - 0 = 2
- Update
mx = max(2, 2) = 2
Window at position [4,5,6]: [1,0,0]
- Add
nums[6] = 0
:cnt = 2 + 0 = 2
- Remove
nums[3] = 1
:cnt = 2 - 1 = 1
- Update
mx = max(2, 1) = 2
Window at position [5,6,0] (wrapping around): [0,0,0]
- Add
nums[7%7] = nums[0] = 0
:cnt = 1 + 0 = 1
- Remove
nums[4] = 1
:cnt = 1 - 1 = 0
- Update
mx = max(2, 0) = 2
Window at position [6,0,1] (wrapping around): [0,0,1]
- Add
nums[8%7] = nums[1] = 1
:cnt = 0 + 1 = 1
- Remove
nums[5] = 0
:cnt = 1 - 0 = 1
- Update
mx = max(2, 1) = 2
Step 4: Calculate minimum swaps
- Maximum 1s found in any window of size 3:
mx = 2
- Minimum swaps needed:
k - mx = 3 - 2 = 1
The best window found was [1,0,1]
(or [0,1,1]
) which already contains 2 ones. We only need 1 swap to bring the remaining 1 into this window. For example, we could swap the 0 at position 2 with the 1 at position 4 to get all 1s grouped together.
Solution Implementation
1class Solution:
2 def minSwaps(self, nums: List[int]) -> int:
3 # Count total number of 1s in the array - this will be our window size
4 total_ones = nums.count(1)
5
6 # Calculate the number of 1s in the initial window of size total_ones
7 current_ones_in_window = sum(nums[:total_ones])
8 max_ones_in_window = current_ones_in_window
9
10 # Get array length for circular indexing
11 array_length = len(nums)
12
13 # Slide the window through the circular array
14 # We go from index total_ones to array_length + total_ones to cover full circle
15 for window_end in range(total_ones, array_length + total_ones):
16 # Add the new element entering the window (using modulo for circular array)
17 current_ones_in_window += nums[window_end % array_length]
18
19 # Remove the element leaving the window (using modulo for circular array)
20 window_start = window_end - total_ones
21 current_ones_in_window -= nums[window_start % array_length]
22
23 # Track the maximum number of 1s found in any window
24 max_ones_in_window = max(max_ones_in_window, current_ones_in_window)
25
26 # Minimum swaps needed = total 1s - maximum 1s already in a window
27 # (We need to swap out the 0s in the best window)
28 return total_ones - max_ones_in_window
29
1class Solution {
2 public int minSwaps(int[] nums) {
3 // Calculate total number of 1s in the array (this will be our window size)
4 int windowSize = Arrays.stream(nums).sum();
5 int arrayLength = nums.length;
6
7 // Count the number of 1s in the initial window of size k
8 int onesInCurrentWindow = 0;
9 for (int i = 0; i < windowSize; ++i) {
10 onesInCurrentWindow += nums[i];
11 }
12
13 // Track the maximum number of 1s found in any window
14 int maxOnesInWindow = onesInCurrentWindow;
15
16 // Slide the window through the circular array
17 // We go from windowSize to arrayLength + windowSize to handle circular nature
18 for (int i = windowSize; i < arrayLength + windowSize; ++i) {
19 // Add the new element entering the window (using modulo for circular array)
20 // Subtract the element leaving the window (using modulo for circular array)
21 onesInCurrentWindow += nums[i % arrayLength] - nums[(i - windowSize + arrayLength) % arrayLength];
22
23 // Update the maximum number of 1s found
24 maxOnesInWindow = Math.max(maxOnesInWindow, onesInCurrentWindow);
25 }
26
27 // Minimum swaps needed = total 1s - maximum 1s already in a window
28 // This represents how many 0s need to be swapped out
29 return windowSize - maxOnesInWindow;
30 }
31}
32
1class Solution {
2public:
3 int minSwaps(vector<int>& nums) {
4 // Count total number of 1s in the array
5 int totalOnes = accumulate(nums.begin(), nums.end(), 0);
6 int arraySize = nums.size();
7
8 // Count number of 1s in the first window of size totalOnes
9 int onesInWindow = accumulate(nums.begin(), nums.begin() + totalOnes, 0);
10 int maxOnesInWindow = onesInWindow;
11
12 // Slide the window through the circular array
13 // We need to check all possible windows of size totalOnes
14 for (int i = totalOnes; i < arraySize + totalOnes; ++i) {
15 // Add the new element entering the window (right side)
16 // Remove the element leaving the window (left side)
17 // Using modulo to handle circular array
18 onesInWindow += nums[i % arraySize] - nums[(i - totalOnes + arraySize) % arraySize];
19
20 // Track the maximum number of 1s found in any window
21 maxOnesInWindow = max(maxOnesInWindow, onesInWindow);
22 }
23
24 // Minimum swaps needed = total 1s - maximum 1s already in a window
25 // This represents how many 0s need to be swapped out
26 return totalOnes - maxOnesInWindow;
27 }
28};
29
1/**
2 * Finds the minimum number of swaps needed to group all 1s together in a circular array
3 * @param nums - The input array containing 0s and 1s
4 * @returns The minimum number of swaps required
5 */
6function minSwaps(nums: number[]): number {
7 // Get the length of the array
8 const arrayLength: number = nums.length;
9
10 // Count the total number of 1s in the array (this will be our window size)
11 const totalOnes: number = nums.reduce((sum: number, num: number) => sum + num, 0);
12
13 // Calculate the number of 0s in the initial window of size totalOnes
14 // This represents the number of swaps needed for the first window
15 let currentZeros: number = totalOnes - nums.slice(0, totalOnes).reduce((sum: number, num: number) => sum + num, 0);
16
17 // Initialize the minimum swaps with the current count
18 let minSwapsNeeded: number = currentZeros;
19
20 // Slide the window through the circular array
21 // We go from totalOnes to arrayLength + totalOnes to handle the circular nature
22 for (let i: number = totalOnes; i < arrayLength + totalOnes; i++) {
23 // Update the count by removing the element leaving the window and adding the new element
24 // nums[i - totalOnes] is the element leaving the window
25 // nums[i % arrayLength] is the element entering the window (using modulo for circular array)
26 currentZeros += nums[i - totalOnes] - nums[i % arrayLength];
27
28 // Update the minimum if we found a better configuration
29 minSwapsNeeded = Math.min(minSwapsNeeded, currentZeros);
30 }
31
32 return minSwapsNeeded;
33}
34
Time and Space Complexity
Time Complexity: O(n)
, where n
is the length of the array nums
.
The algorithm performs the following operations:
nums.count(1)
traverses the entire array once:O(n)
sum(nums[:k])
sums at mostn
elements:O(k)
wherek ≤ n
- The for loop iterates exactly
k
times:O(k)
wherek ≤ n
- Inside each iteration, we perform constant time operations (array access, addition, subtraction, comparison):
O(1)
Total time complexity: O(n) + O(k) + O(k) × O(1) = O(n)
since k ≤ n
.
Space Complexity: O(1)
The algorithm uses only a fixed number of variables:
k
: stores the count of 1smx
: tracks the maximum countcnt
: maintains the current window sumn
: stores the array lengthi
: loop counter
No additional data structures are created that scale with the input size, resulting in constant space complexity.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Forgetting the Circular Nature of the Array
Pitfall: A common mistake is treating the array as linear and only checking windows from index 0 to n-k, missing potential optimal windows that wrap around the array.
Example: For nums = [1,1,0,0,1]
, if you only check windows [0:3], [1:4], [2:5], you'd miss the optimal circular window [3:5] + [0:1] which contains all three 1s.
Solution: Always iterate through n
complete windows by extending the loop to range(k, n + k)
and using modulo arithmetic for indexing.
2. Incorrect Modulo Calculation for Window Start
Pitfall: When calculating the element leaving the window, using (i - k) % n
instead of (i - k + n) % n
can produce negative indices in Python, leading to incorrect array access.
Example: When i = 2, k = 3, n = 5:
- Wrong:
(2 - 3) % 5 = -1
(accesses the last element incorrectly) - Correct:
(2 - 3 + 5) % 5 = 4
(correctly wraps to index 4)
Solution: Always add n
before taking modulo to ensure positive indices: (i - k + n) % n
.
3. Off-by-One Error in Initial Window
Pitfall: Incorrectly initializing the first window sum, such as using sum(nums[:k-1])
or sum(nums[:k+1])
.
Example: If k = 3 and nums = [1,0,1,0,1]
:
- Wrong:
sum(nums[:2])
only sums 2 elements - Correct:
sum(nums[:3])
correctly sums the first 3 elements
Solution: Use sum(nums[:k])
for the initial window, remembering that Python slicing is exclusive of the end index.
4. Edge Case: All 1s or All 0s
Pitfall: Not handling arrays that contain all 1s or all 0s, which could lead to unnecessary calculations or incorrect results.
Example:
nums = [1,1,1,1]
: Should return 0 immediatelynums = [0,0,0,0]
: Should return 0 immediately
Solution: Add early return conditions:
if total_ones == 0 or total_ones == array_length: return 0
5. Using Wrong Variable Names
Pitfall: In the provided code, variable names could be confusing. Using generic names like i
, k
, mx
, cnt
makes the code harder to debug and understand.
Solution: Use descriptive variable names:
total_ones = nums.count(1)
current_ones_in_window = sum(nums[:total_ones])
max_ones_in_window = current_ones_in_window
Which technique can we use to find the middle of a linked list?
Recommended Readings
https assets algo monster cover_photos stack svg Sliding Window Maximum Monotonic Stack We have an array and a sliding window defined by a start index and an end index The sliding window moves from left of the array to right There are always k elements in the window The window
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Don’t Miss This!