2934. Minimum Operations to Maximize Last Elements in Arrays
Problem Description
You have two arrays nums1
and nums2
, both with the same length n
and 0-indexed. Your goal is to make sure that:
- The last element of
nums1
(at indexn-1
) is the maximum value innums1
- The last element of
nums2
(at indexn-1
) is the maximum value innums2
You can perform swap operations to achieve this goal. In each operation, you pick an index i
(from 0 to n-1
) and swap the values between nums1[i]
and nums2[i]
.
The problem asks you to find the minimum number of operations needed to satisfy both conditions. If it's impossible to achieve both conditions, return -1
.
For example, if you have:
nums1 = [1, 2, 7]
andnums2 = [4, 5, 3]
- The last elements are
nums1[2] = 7
andnums2[2] = 3
- Currently,
7
is already the maximum innums1
, but3
is not the maximum innums2
(since5 > 3
) - You could swap at index 2 to get
nums1 = [1, 2, 3]
andnums2 = [4, 5, 7]
- But now
3
is not the maximum innums1
- So you'd need to check other swap combinations to find the minimum operations
The solution considers two main scenarios:
- Keep the last elements as they are and swap other elements as needed
- Swap the last elements first, then swap other elements as needed
For each scenario, it checks if swapping elements at positions 0
to n-2
can satisfy the conditions while ensuring all elements remain less than or equal to their respective array's last element.
Intuition
The key insight is that the last elements of both arrays must be the maximum values in their respective arrays. This means every other element in nums1
must be ≤ nums1[n-1]
and every other element in nums2
must be ≤ nums2[n-1]
.
Since we can only swap elements at the same index between the two arrays, we need to think about what our final configuration should look like. There are only two possible end states for the last position:
- Keep
nums1[n-1]
andnums2[n-1]
as they are - Swap them so that
nums1[n-1]
becomes the originalnums2[n-1]
and vice versa
Why only these two cases? Because once we decide what values should be at the last position (which must be the maximums), all other decisions are forced upon us. For each position i
from 0
to n-2
, we have two values available: nums1[i]
and nums2[i]
. We need to distribute them such that one goes to the first array and one goes to the second array, ensuring they don't exceed their respective maximums.
For each position i
(except the last), we check:
- If
nums1[i] ≤ x
andnums2[i] ≤ y
(wherex
andy
are our chosen last elements), we don't need to swap - If
nums1[i] ≤ y
andnums2[i] ≤ x
, we must swap at positioni
- If neither condition holds, it's impossible to satisfy the requirements
The greedy approach works here because at each position, we have at most one valid choice (swap or don't swap). We're not making a choice that could be optimized later - either we must swap, must not swap, or the configuration is impossible.
By calculating the minimum swaps needed for both possible end configurations and taking the minimum (accounting for whether we swapped the last position), we get our answer.
Solution Approach
The solution implements a helper function f(x, y)
that calculates the minimum number of swaps needed when we fix the last elements as x
for nums1
and y
for nums2
.
Helper Function Logic:
The function f(x, y)
iterates through the first n-1
positions and counts how many swaps are needed:
- For each pair
(a, b)
from(nums1[i], nums2[i])
wherei < n-1
:- If
a ≤ x
andb ≤ y
: No swap needed, continue to next position - If
a ≤ y
andb ≤ x
: A swap is required, increment counter by 1 - If neither condition is satisfied: Return
-1
(impossible configuration)
- If
- Return the total count of swaps needed
Main Algorithm:
-
Case 1 - No swap at last position:
- Call
a = f(nums1[-1], nums2[-1])
- This calculates swaps needed when keeping the last elements as they are
- The last elements are
nums1[n-1]
andnums2[n-1]
- Call
-
Case 2 - Swap at last position:
- Call
b = f(nums2[-1], nums1[-1])
- This calculates swaps needed when the last elements are swapped
- The last elements become
nums2[n-1]
innums1
andnums1[n-1]
innums2
- Note: If we choose this case, we need
b + 1
total operations (the extra 1 for swapping the last position)
- Call
-
Result Determination:
- If both
a = -1
andb = -1
(meaninga + b = -2
): Return-1
(impossible) - Otherwise: Return
min(a, b + 1)
a
represents operations for Case 1b + 1
represents operations for Case 2 (including the swap at last position)
- If both
The time complexity is O(n)
as we traverse the arrays twice (once for each case), and the space complexity is O(1)
as we only use a few variables to track the counts.
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 a concrete example to illustrate the solution approach.
Example: nums1 = [3, 7, 9]
and nums2 = [8, 2, 4]
Our goal is to make nums1[2]
the maximum of nums1
and nums2[2]
the maximum of nums2
.
Step 1: Analyze current state
- Last elements:
nums1[2] = 9
,nums2[2] = 4
- Max in
nums1
:max(3, 7, 9) = 9
✓ (already satisfied) - Max in
nums2
:max(8, 2, 4) = 8
✗ (not satisfied, since 8 > 4)
Step 2: Try Case 1 - Keep last elements as they are
- Target:
nums1
elements ≤ 9,nums2
elements ≤ 4 - Check position 0:
nums1[0] = 3 ≤ 9
✓ andnums2[0] = 8 ≤ 4
✗- Can we swap? After swap:
8 ≤ 9
✓ and3 ≤ 4
✓ - Yes! Need to swap position 0
- Can we swap? After swap:
- Check position 1:
nums1[1] = 7 ≤ 9
✓ andnums2[1] = 2 ≤ 4
✓- No swap needed
- Result for Case 1: 1 swap (at position 0)
- Arrays become:
nums1 = [8, 7, 9]
,nums2 = [3, 2, 4]
Step 3: Try Case 2 - Swap last elements first
- After swapping last position:
nums1[2] = 4
,nums2[2] = 9
- Target:
nums1
elements ≤ 4,nums2
elements ≤ 9 - Check position 0:
nums1[0] = 3 ≤ 4
✓ andnums2[0] = 8 ≤ 9
✓- No swap needed
- Check position 1:
nums1[1] = 7 ≤ 4
✗ andnums2[1] = 2 ≤ 9
✓- Can we swap? After swap:
2 ≤ 4
✓ and7 ≤ 9
✓ - Yes! Need to swap position 1
- Can we swap? After swap:
- Result for Case 2: 1 swap (at position 1) + 1 swap (at last position) = 2 swaps
- Arrays become:
nums1 = [3, 2, 4]
,nums2 = [8, 7, 9]
Step 4: Determine minimum
- Case 1: 1 swap
- Case 2: 2 swaps
- Answer: 1 (minimum of the two cases)
The final configuration using Case 1 is:
nums1 = [8, 7, 9]
wheremax = 9
✓nums2 = [3, 2, 4]
wheremax = 4
✓
Solution Implementation
1class Solution:
2 def minOperations(self, nums1: List[int], nums2: List[int]) -> int:
3 def count_swaps_needed(max_val1: int, max_val2: int) -> int:
4 """
5 Count minimum swaps needed to make all elements in nums1 <= max_val1
6 and all elements in nums2 <= max_val2 (excluding last elements).
7
8 Returns -1 if impossible, otherwise returns the number of swaps needed.
9 """
10 swap_count = 0
11
12 # Check each pair of elements (excluding the last pair)
13 for num1, num2 in zip(nums1[:-1], nums2[:-1]):
14 # No swap needed if already satisfies the condition
15 if num1 <= max_val1 and num2 <= max_val2:
16 continue
17
18 # Check if swapping would satisfy the condition
19 if not (num1 <= max_val2 and num2 <= max_val1):
20 # Impossible to satisfy even with swapping
21 return -1
22
23 # Swap is needed and valid
24 swap_count += 1
25
26 return swap_count
27
28 # Case 1: Keep last elements as they are (nums1[-1], nums2[-1])
29 swaps_without_last_swap = count_swaps_needed(nums1[-1], nums2[-1])
30
31 # Case 2: Swap the last elements (nums2[-1], nums1[-1])
32 swaps_with_last_swap = count_swaps_needed(nums2[-1], nums1[-1])
33
34 # If both cases are impossible, return -1
35 if swaps_without_last_swap == -1 and swaps_with_last_swap == -1:
36 return -1
37
38 # If one case is impossible, return the other
39 if swaps_without_last_swap == -1:
40 return swaps_with_last_swap + 1
41 if swaps_with_last_swap == -1:
42 return swaps_without_last_swap
43
44 # Return minimum of both valid cases
45 # Add 1 to swaps_with_last_swap to account for swapping the last pair
46 return min(swaps_without_last_swap, swaps_with_last_swap + 1)
47
1class Solution {
2 private int arrayLength;
3
4 /**
5 * Finds the minimum number of operations to make arrays satisfy the condition
6 * where nums1[i] <= nums1[n-1] and nums2[i] <= nums2[n-1] for all i
7 * Operations: swap nums1[i] with nums2[i]
8 *
9 * @param nums1 First input array
10 * @param nums2 Second input array
11 * @return Minimum operations needed, or -1 if impossible
12 */
13 public int minOperations(int[] nums1, int[] nums2) {
14 arrayLength = nums1.length;
15
16 // Case 1: Keep last elements as they are
17 int operationsWithoutSwappingLast = countSwapsNeeded(
18 nums1, nums2, nums1[arrayLength - 1], nums2[arrayLength - 1]
19 );
20
21 // Case 2: Swap the last elements
22 int operationsWithSwappingLast = countSwapsNeeded(
23 nums1, nums2, nums2[arrayLength - 1], nums1[arrayLength - 1]
24 );
25
26 // Both cases impossible
27 if (operationsWithoutSwappingLast == -1 && operationsWithSwappingLast == -1) {
28 return -1;
29 }
30
31 // Return minimum of valid cases (add 1 for swapping last elements in case 2)
32 if (operationsWithoutSwappingLast == -1) {
33 return operationsWithSwappingLast + 1;
34 }
35 if (operationsWithSwappingLast == -1) {
36 return operationsWithoutSwappingLast;
37 }
38 return Math.min(operationsWithoutSwappingLast, operationsWithSwappingLast + 1);
39 }
40
41 /**
42 * Counts the number of swaps needed to satisfy the condition
43 * where all nums1[i] <= maxForNums1 and nums2[i] <= maxForNums2
44 *
45 * @param nums1 First array
46 * @param nums2 Second array
47 * @param maxForNums1 Maximum allowed value for nums1 elements
48 * @param maxForNums2 Maximum allowed value for nums2 elements
49 * @return Number of swaps needed, or -1 if impossible
50 */
51 private int countSwapsNeeded(int[] nums1, int[] nums2, int maxForNums1, int maxForNums2) {
52 int swapCount = 0;
53
54 // Check each position except the last one
55 for (int i = 0; i < arrayLength - 1; i++) {
56 // Current values already satisfy the condition
57 if (nums1[i] <= maxForNums1 && nums2[i] <= maxForNums2) {
58 continue;
59 }
60
61 // Check if swapping would satisfy the condition
62 if (nums1[i] <= maxForNums2 && nums2[i] <= maxForNums1) {
63 swapCount++;
64 } else {
65 // Neither keeping nor swapping works
66 return -1;
67 }
68 }
69
70 return swapCount;
71 }
72}
73
1class Solution {
2public:
3 int minOperations(vector<int>& nums1, vector<int>& nums2) {
4 int n = nums1.size();
5
6 // Lambda function to count minimum swaps needed
7 // to make all elements <= maxNum1 in nums1 and <= maxNum2 in nums2
8 auto countSwaps = [&](int maxNum1, int maxNum2) {
9 int swapCount = 0;
10
11 // Check each position except the last one
12 for (int i = 0; i < n - 1; ++i) {
13 // No swap needed if current values already satisfy the condition
14 if (nums1[i] <= maxNum1 && nums2[i] <= maxNum2) {
15 continue;
16 }
17
18 // Check if swapping would satisfy the condition
19 if (!(nums1[i] <= maxNum2 && nums2[i] <= maxNum1)) {
20 return -1; // Impossible to satisfy even with swap
21 }
22
23 // Swap is needed at this position
24 ++swapCount;
25 }
26
27 return swapCount;
28 };
29
30 // Case 1: Keep last elements as they are
31 int swapsWithoutLastSwap = countSwaps(nums1.back(), nums2.back());
32
33 // Case 2: Swap the last elements
34 int swapsWithLastSwap = countSwaps(nums2.back(), nums1.back());
35
36 // If both cases are impossible, return -1
37 if (swapsWithoutLastSwap == -1 && swapsWithLastSwap == -1) {
38 return -1;
39 }
40
41 // Return minimum of valid cases
42 // Add 1 to swapsWithLastSwap to account for swapping the last elements
43 if (swapsWithoutLastSwap == -1) {
44 return swapsWithLastSwap + 1;
45 }
46 if (swapsWithLastSwap == -1) {
47 return swapsWithoutLastSwap;
48 }
49 return min(swapsWithoutLastSwap, swapsWithLastSwap + 1);
50 }
51};
52
1/**
2 * Calculates the minimum number of operations needed to make both arrays non-decreasing
3 * by swapping elements at the same index between nums1 and nums2.
4 * The last elements of both arrays are fixed and cannot be swapped.
5 *
6 * @param nums1 - First array of numbers
7 * @param nums2 - Second array of numbers
8 * @returns Minimum number of swaps needed, or -1 if impossible
9 */
10function minOperations(nums1: number[], nums2: number[]): number {
11 const arrayLength: number = nums1.length;
12
13 /**
14 * Counts the minimum swaps needed to ensure all elements before the last position
15 * are less than or equal to the given max values for each array
16 *
17 * @param maxForNums1 - Maximum allowed value for nums1 elements
18 * @param maxForNums2 - Maximum allowed value for nums2 elements
19 * @returns Number of swaps needed, or -1 if impossible
20 */
21 const countSwapsNeeded = (maxForNums1: number, maxForNums2: number): number => {
22 let swapCount: number = 0;
23
24 // Check each element except the last one
25 for (let i = 0; i < arrayLength - 1; i++) {
26 // If current elements already satisfy the constraints, no swap needed
27 if (nums1[i] <= maxForNums1 && nums2[i] <= maxForNums2) {
28 continue;
29 }
30
31 // Check if swapping would satisfy the constraints
32 if (!(nums1[i] <= maxForNums2 && nums2[i] <= maxForNums1)) {
33 // Neither original nor swapped positions work
34 return -1;
35 }
36
37 // Swap is needed and valid
38 swapCount++;
39 }
40
41 return swapCount;
42 };
43
44 // Get the last elements of both arrays
45 const lastElementNums1: number = nums1.at(-1)!;
46 const lastElementNums2: number = nums2.at(-1)!;
47
48 // Case 1: Keep last elements as they are
49 const swapsWithoutLastSwap: number = countSwapsNeeded(lastElementNums1, lastElementNums2);
50
51 // Case 2: Swap the last elements
52 const swapsWithLastSwap: number = countSwapsNeeded(lastElementNums2, lastElementNums1);
53
54 // If both cases are impossible, return -1
55 if (swapsWithoutLastSwap === -1 && swapsWithLastSwap === -1) {
56 return -1;
57 }
58
59 // Return the minimum between:
60 // - swapsWithoutLastSwap (no swap at last position)
61 // - swapsWithLastSwap + 1 (includes the swap at last position)
62 if (swapsWithoutLastSwap === -1) {
63 return swapsWithLastSwap + 1;
64 }
65 if (swapsWithLastSwap === -1) {
66 return swapsWithoutLastSwap;
67 }
68 return Math.min(swapsWithoutLastSwap, swapsWithLastSwap + 1);
69}
70
Time and Space Complexity
The time complexity is O(n)
, where n
is the length of the arrays nums1
and nums2
. The function f
is called twice, and each call iterates through the arrays once using zip(nums1[:-1], nums2[:-1])
, which processes n-1
elements. Since we iterate through the arrays a constant number of times (twice), the overall time complexity remains O(n)
.
The space complexity is O(1)
. The algorithm only uses a constant amount of extra space for variables like cnt
, a
, and b
. The zip
function creates an iterator that doesn't consume additional space proportional to the input size, and no additional data structures are created that scale with the input size.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Forgetting to Add 1 for the Last Swap in Case 2
One of the most common mistakes is forgetting that when we swap the last elements (Case 2), we need to count that swap as an additional operation.
Incorrect Implementation:
# WRONG: Forgetting to add 1 for the last swap
return min(swaps_without_last_swap, swaps_with_last_swap) # Missing +1
Correct Implementation:
# CORRECT: Account for swapping the last elements
return min(swaps_without_last_swap, swaps_with_last_swap + 1)
2. Incorrect Handling of Impossible Cases
Another pitfall is not properly handling the edge cases when one or both scenarios return -1.
Incorrect Implementation:
# WRONG: This will return incorrect results when one case is -1
return min(swaps_without_last_swap, swaps_with_last_swap + 1)
# If swaps_without_last_swap = -1, min(-1, valid_number) returns -1 incorrectly
Correct Implementation:
# CORRECT: Check for impossible cases first
if swaps_without_last_swap == -1 and swaps_with_last_swap == -1:
return -1
if swaps_without_last_swap == -1:
return swaps_with_last_swap + 1
if swaps_with_last_swap == -1:
return swaps_without_last_swap
return min(swaps_without_last_swap, swaps_with_last_swap + 1)
3. Confusing the Swap Logic in the Helper Function
It's easy to mix up the conditions for when a swap is needed versus when it's impossible.
Incorrect Logic:
# WRONG: Checking wrong conditions if num1 <= max_val1 or num2 <= max_val2: # Using OR instead of AND continue
Correct Logic:
# CORRECT: Both conditions must be satisfied without swap if num1 <= max_val1 and num2 <= max_val2: continue # Check if swap would help if num1 <= max_val2 and num2 <= max_val1: swap_count += 1 else: return -1 # Impossible even with swap
4. Including the Last Elements in the Iteration
The helper function should only check elements from index 0 to n-2, not including the last elements.
Incorrect Implementation:
# WRONG: Including last elements in the check
for num1, num2 in zip(nums1, nums2): # Iterates through ALL elements
# This would incorrectly process the last pair
Correct Implementation:
# CORRECT: Exclude the last elements
for num1, num2 in zip(nums1[:-1], nums2[:-1]):
# Only processes elements before the last position
These pitfalls can lead to incorrect results or runtime errors. Always remember to carefully consider the swap counting logic and edge case handling when implementing this type of algorithm.
You are given an array of intervals where intervals[i] = [start_i, end_i]
represent the start and end of the ith
interval. You need to merge all overlapping intervals and return an array of the non-overlapping intervals that cover all the intervals in the input.
Recommended Readings
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
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!