801. Minimum Swaps To Make Sequences Increasing
Problem Description
You have two integer arrays nums1
and nums2
of equal length. You can perform swap operations where you exchange nums1[i]
with nums2[i]
at any index i
.
For example, if nums1 = [1,2,3,8]
and nums2 = [5,6,7,4]
, swapping at index 3 would give you nums1 = [1,2,3,4]
and nums2 = [5,6,7,8]
.
Your goal is to make both arrays strictly increasing using the minimum number of swap operations. A strictly increasing array means each element is strictly less than the next one: arr[0] < arr[1] < arr[2] < ... < arr[arr.length - 1]
.
The problem guarantees that it's always possible to make both arrays strictly increasing with the given input.
You need to return the minimum number of swaps required to achieve this.
Intuition
When looking at this problem, we need to track the state of our arrays as we process each position. At any index i
, we have two choices: either swap the elements at position i
or keep them as they are.
The key insight is that our decision at position i
depends on what happened at position i-1
. If we swapped at i-1
, this affects what we can do at position i
to maintain the strictly increasing property.
This suggests a dynamic programming approach where we track two states:
a
: minimum swaps needed if we DON'T swap at positioni
b
: minimum swaps needed if we DO swap at positioni
For each position, we need to consider the relationship between consecutive elements. There are two main scenarios:
-
Forced swap scenario: When
nums1[i-1] >= nums1[i]
ornums2[i-1] >= nums2[i]
, the current arrangement violates the strictly increasing property. We must change the relative positions - if we swapped ati-1
, we can't swap ati
, and vice versa. -
Flexible scenario: When both arrays are already increasing (
nums1[i-1] < nums1[i]
andnums2[i-1] < nums2[i]
), we have more freedom. We can maintain the same swap pattern fromi-1
. Additionally, if cross-comparisons also work (nums1[i-1] < nums2[i]
andnums2[i-1] < nums1[i]
), we can change the swap pattern, giving us the opportunity to choose the minimum cost option.
By building up these minimum costs position by position, we eventually find the global minimum number of swaps needed for the entire arrays.
Learn more about Dynamic Programming patterns.
Solution Approach
We implement a dynamic programming solution using two variables a
and b
to track the minimum swaps needed:
a
: minimum swaps to make arrays strictly increasing up to indexi
without swapping at positioni
b
: minimum swaps to make arrays strictly increasing up to indexi
with swapping at positioni
Initialization:
a = 0
: no swap needed at index 0 if we don't swapb = 1
: one swap needed at index 0 if we do swap
Processing each position i
from 1 to n-1:
-
Save previous states:
x = a
andy = b
to remember the minimum swaps up to positioni-1
-
Check if positions
i-1
andi
violate the strictly increasing property:- If
nums1[i-1] >= nums1[i]
ornums2[i-1] >= nums2[i]
:- The relative positions must change between
i-1
andi
- If we swapped at
i-1
(costy
), we can't swap ati
:a = y
- If we didn't swap at
i-1
(costx
), we must swap ati
:b = x + 1
- The relative positions must change between
- If
-
Otherwise, both arrays are already increasing at these positions:
- We can maintain the swap pattern:
b = y + 1
(if we swapped ati-1
, we swap ati
too) - Check if cross-swapping is valid: if
nums1[i-1] < nums2[i]
andnums2[i-1] < nums1[i]
:- We can change the swap pattern between
i-1
andi
- Take the minimum:
a = min(a, y)
(compare not swapping ati
with different patterns) - Take the minimum:
b = min(b, x + 1)
(compare swapping ati
with different patterns)
- We can change the swap pattern between
- We can maintain the swap pattern:
Final Result:
Return min(a, b)
- the minimum between not swapping and swapping at the last position.
The algorithm runs in O(n)
time with O(1)
space, as we only maintain two variables throughout the iteration.
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 algorithm with nums1 = [1,3,5,4]
and nums2 = [1,2,3,7]
.
We need to track two states:
a
: minimum swaps if we DON'T swap at current positionb
: minimum swaps if we DO swap at current position
Initial state (i=0):
a = 0
(no swap at position 0)b = 1
(swap at position 0)
Position i=1:
- Previous values:
x = 0
,y = 1
- Check if arrays violate strictly increasing:
nums1[0] < nums1[1]
(1 < 3 ✓) andnums2[0] < nums2[1]
(1 < 2 ✓) - No violation, so we have flexibility
a = x = 0
(don't swap at i=1, didn't swap at i=0)b = y + 1 = 2
(swap at i=1, swapped at i=0)- Check cross-swap validity:
nums1[0] < nums2[1]
(1 < 2 ✓) andnums2[0] < nums1[1]
(1 < 3 ✓) - Cross-swap is valid, so we can optimize:
a = min(0, 1) = 0
(compare not swapping with different patterns)b = min(2, 0+1) = 1
(swapping at i=1 without swapping at i=0 is better)
Position i=2:
- Previous values:
x = 0
,y = 1
- Check:
nums1[1] < nums1[2]
(3 < 5 ✓) andnums2[1] < nums2[2]
(2 < 3 ✓) - No violation, flexibility exists
a = x = 0
(maintain no-swap pattern)b = y + 1 = 2
(maintain swap pattern)- Check cross-swap:
nums1[1] < nums2[2]
(3 < 3 ✗) - fails! - Cannot optimize with cross-swap, keep current values:
a = 0
,b = 2
Position i=3:
- Previous values:
x = 0
,y = 2
- Check:
nums1[2] >= nums1[3]
(5 >= 4 ✓) - violation detected! - Must change relative positions:
a = y = 2
(if we swapped at i=2, don't swap at i=3)b = x + 1 = 1
(if we didn't swap at i=2, must swap at i=3)
Final Result:
min(a, b) = min(2, 1) = 1
The minimum number of swaps needed is 1. We swap at position 3, transforming:
nums1 = [1,3,5,7]
nums2 = [1,2,3,4]
Both arrays are now strictly increasing.
Solution Implementation
1class Solution:
2 def minSwap(self, nums1: List[int], nums2: List[int]) -> int:
3 # Dynamic Programming approach to find minimum swaps
4 # keep: minimum swaps needed if we DON'T swap at current position
5 # swap: minimum swaps needed if we DO swap at current position
6 keep = 0 # No swap needed at position 0 to keep arrays valid
7 swap = 1 # One swap needed if we swap at position 0
8
9 # Iterate through arrays starting from index 1
10 for i in range(1, len(nums1)):
11 # Store previous state values
12 prev_keep = keep
13 prev_swap = swap
14
15 # Check if arrays are NOT naturally increasing without any swap
16 if nums1[i - 1] >= nums1[i] or nums2[i - 1] >= nums2[i]:
17 # Arrays are not naturally increasing
18 # Must swap at exactly one position (either previous or current)
19 keep = prev_swap # Don't swap current, so must have swapped previous
20 swap = prev_keep + 1 # Swap current, so previous must not be swapped
21 else:
22 # Arrays are naturally increasing without swap
23 # Default: continue the same state as previous
24 swap = prev_swap + 1 # If we swapped previous, swap current too
25
26 # Check if cross-comparison also works
27 # (previous nums1 with current nums2, and previous nums2 with current nums1)
28 if nums1[i - 1] < nums2[i] and nums2[i - 1] < nums1[i]:
29 # Cross-swap is also valid
30 # We can choose to swap or not swap independently
31 keep = min(keep, prev_swap) # Can keep current regardless of previous
32 swap = min(swap, prev_keep + 1) # Can swap current regardless of previous
33
34 # Return minimum of both final states
35 return min(keep, swap)
36
1class Solution {
2 public int minSwap(int[] nums1, int[] nums2) {
3 // Dynamic Programming approach to find minimum swaps needed
4 // to make both arrays strictly increasing
5
6 // noSwapCurrent: minimum swaps needed if we DON'T swap at current position
7 // swapCurrent: minimum swaps needed if we DO swap at current position
8 int noSwapCurrent = 0;
9 int swapCurrent = 1;
10
11 // Iterate through both arrays starting from index 1
12 for (int i = 1; i < nums1.length; i++) {
13 // Store previous state values
14 int noSwapPrevious = noSwapCurrent;
15 int swapPrevious = swapCurrent;
16
17 // Check if arrays are NOT naturally increasing at position i
18 // (violates strictly increasing property without any swaps)
19 if (nums1[i - 1] >= nums1[i] || nums2[i - 1] >= nums2[i]) {
20 // Must swap at exactly one position (either previous or current)
21 // If we don't swap current, we must have swapped previous
22 noSwapCurrent = swapPrevious;
23 // If we swap current, we must NOT have swapped previous
24 swapCurrent = noSwapPrevious + 1;
25 } else {
26 // Arrays are naturally increasing at position i
27 // We have flexibility in our choices
28
29 // If we swap current, we need previous to be swapped too
30 // to maintain strictly increasing property
31 swapCurrent = swapPrevious + 1;
32
33 // Check if cross-comparison also maintains strictly increasing
34 // (nums1[i-1] can go before nums2[i] AND nums2[i-1] can go before nums1[i])
35 if (nums1[i - 1] < nums2[i] && nums2[i - 1] < nums1[i]) {
36 // We have even more flexibility - can mix swap states
37 // If we don't swap current, we can choose the minimum from:
38 // - not swapping previous (noSwapPrevious)
39 // - swapping previous (swapPrevious)
40 noSwapCurrent = Math.min(noSwapCurrent, swapPrevious);
41 // If we swap current, we can choose the minimum from:
42 // - swapping when previous was also swapped (swapPrevious + 1)
43 // - swapping when previous was not swapped (noSwapPrevious + 1)
44 swapCurrent = Math.min(swapCurrent, noSwapPrevious + 1);
45 }
46 }
47 }
48
49 // Return the minimum swaps needed (either ending with swap or no swap)
50 return Math.min(noSwapCurrent, swapCurrent);
51 }
52}
53
1class Solution {
2public:
3 int minSwap(vector<int>& nums1, vector<int>& nums2) {
4 // Dynamic Programming approach to find minimum swaps
5 // noSwap: minimum swaps needed if we don't swap at current position
6 // swap: minimum swaps needed if we swap at current position
7 int noSwap = 0;
8 int swap = 1;
9 int n = nums1.size();
10
11 // Iterate through each position starting from index 1
12 for (int i = 1; i < n; ++i) {
13 // Store previous state values
14 int prevNoSwap = noSwap;
15 int prevSwap = swap;
16
17 // Check if arrays are not strictly increasing without any operation
18 if (nums1[i - 1] >= nums1[i] || nums2[i - 1] >= nums2[i]) {
19 // Must swap at position i-1 or position i to maintain strict increasing
20 // If we don't swap at i, we must have swapped at i-1
21 noSwap = prevSwap;
22 // If we swap at i, we must not have swapped at i-1
23 swap = prevNoSwap + 1;
24 } else {
25 // Arrays are already strictly increasing
26 // Keep the previous no-swap state
27 // noSwap = prevNoSwap; (implicitly done, no change needed)
28 // If we swap at i, add 1 to previous swap count
29 swap = prevSwap + 1;
30
31 // Check if cross-comparison also maintains strict increasing
32 // This means we can swap at i-1 and not at i, or vice versa
33 if (nums1[i - 1] < nums2[i] && nums2[i - 1] < nums1[i]) {
34 // We have flexibility in swapping
35 // Choose minimum for not swapping at i
36 noSwap = min(noSwap, prevSwap);
37 // Choose minimum for swapping at i
38 swap = min(swap, prevNoSwap + 1);
39 }
40 }
41 }
42
43 // Return the minimum between not swapping and swapping at the last position
44 return min(noSwap, swap);
45 }
46};
47
1/**
2 * Finds the minimum number of swaps needed to make both arrays strictly increasing
3 * @param nums1 - First array of numbers
4 * @param nums2 - Second array of numbers
5 * @returns The minimum number of swaps required
6 */
7function minSwap(nums1: number[], nums2: number[]): number {
8 // noSwap: minimum swaps needed if we don't swap at current position
9 // swap: minimum swaps needed if we swap at current position
10 let noSwap: number = 0;
11 let swap: number = 1;
12
13 // Iterate through each position starting from index 1
14 for (let i = 1; i < nums1.length; i++) {
15 // Store previous state values
16 let previousNoSwap: number = noSwap;
17 let previousSwap: number = swap;
18
19 // Check if arrays are not naturally increasing at current position
20 if (nums1[i - 1] >= nums1[i] || nums2[i - 1] >= nums2[i]) {
21 // Must swap at position i-1 or position i (but not both)
22 // If we don't swap at i, we must have swapped at i-1
23 noSwap = previousSwap;
24 // If we swap at i, we must not have swapped at i-1
25 swap = previousNoSwap + 1;
26 } else {
27 // Arrays are naturally increasing, swapping is optional
28 // Keep the same state (swap at i if swapped at i-1)
29 swap = previousSwap + 1;
30
31 // Check if cross-comparison also maintains increasing order
32 if (nums1[i - 1] < nums2[i] && nums2[i - 1] < nums1[i]) {
33 // We can choose to swap or not swap independently
34 // Take minimum of keeping same state or alternating state
35 noSwap = Math.min(noSwap, previousSwap);
36 swap = Math.min(swap, previousNoSwap + 1);
37 }
38 }
39 }
40
41 // Return the minimum between not swapping and swapping at the last position
42 return Math.min(noSwap, swap);
43}
44
Time and Space Complexity
The time complexity of this code is O(n)
, where n
is the length of the input arrays nums1
and nums2
. This is because the algorithm uses a single for loop that iterates through the arrays from index 1 to len(nums1) - 1
, performing a constant amount of work in each iteration. Each iteration involves only comparisons and assignments of variables, which are all O(1)
operations.
The space complexity is O(1)
as the algorithm only uses a fixed number of variables (a
, b
, x
, y
) regardless of the input size. These variables store the minimum number of swaps needed for different states at each position, and they are reused throughout the iteration rather than creating new storage that scales with the input size. No additional data structures like arrays or recursion stacks are used that would grow with the input size.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Incorrectly Updating States In-Place
A critical mistake is updating a
and b
(or keep
and swap
) directly without preserving their previous values for subsequent calculations within the same iteration.
Incorrect Implementation:
for i in range(1, len(nums1)):
if nums1[i-1] >= nums1[i] or nums2[i-1] >= nums2[i]:
keep = swap # Wrong! 'swap' might be modified below
swap = keep + 1 # Using already modified 'keep'!
Why it's wrong: When we update keep = swap
, we lose the original value of keep
. Then when we calculate swap = keep + 1
, we're using the new value of keep
(which is actually the old swap
), not the original keep
value from the previous iteration.
Correct Solution:
for i in range(1, len(nums1)):
prev_keep = keep
prev_swap = swap
if nums1[i-1] >= nums1[i] or nums2[i-1] >= nums2[i]:
keep = prev_swap
swap = prev_keep + 1 # Using preserved original value
2. Missing the Cross-Swap Validation Check
Another common mistake is assuming that if the arrays are naturally increasing (nums1[i-1] < nums1[i]
and nums2[i-1] < nums2[i]
), we can freely choose to swap or not swap at position i
regardless of what happened at position i-1
.
Incorrect Implementation:
else:
# Arrays are naturally increasing
keep = min(prev_keep, prev_swap) # Wrong! Not always valid
swap = min(prev_keep + 1, prev_swap + 1)
Why it's wrong: Just because the arrays are naturally increasing doesn't mean we can mix and match swap decisions. We need to verify that cross-comparisons are valid.
Correct Solution:
else:
# Default: maintain same swap pattern
swap = prev_swap + 1
# Only optimize if cross-swap is valid
if nums1[i-1] < nums2[i] and nums2[i-1] < nums1[i]:
keep = min(keep, prev_swap)
swap = min(swap, prev_keep + 1)
3. Initializing with Wrong Values
Some might initialize both states to 0 or both to 1, misunderstanding what they represent.
Incorrect:
keep = 0 swap = 0 # Wrong! If we swap at index 0, that's 1 operation
Correct:
keep = 0 # No swap at index 0 means 0 operations swap = 1 # Swapping at index 0 means 1 operation
These pitfalls can lead to incorrect minimum swap counts or runtime errors, making it crucial to carefully track state transitions and validate all swap possibilities.
Which data structure is used to implement priority queue?
Recommended Readings
What is Dynamic Programming Prerequisite DFS problems dfs_intro Backtracking problems backtracking Memoization problems memoization_intro Pruning problems backtracking_pruning Dynamic programming is an algorithmic optimization technique that breaks down a complicated problem into smaller overlapping sub problems in a recursive manner and uses solutions to the sub problems to construct a solution
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!