Facebook Pixel

2561. Rearranging Fruits

HardGreedySortArrayHash Table
Leetcode Link

Problem Description

You have two fruit baskets, each containing n fruits. The cost of each fruit is given in two arrays: basket1 and basket2 (both 0-indexed). Your goal is to make both baskets equal by swapping fruits between them.

Swapping Rules:

  • You can swap the i-th fruit from basket1 with the j-th fruit from basket2
  • The cost of each swap is min(basket1[i], basket2[j]) - you pay the cost of the cheaper fruit being swapped

What makes baskets equal? Two baskets are considered equal if, after sorting both baskets by fruit cost, they contain exactly the same sequence of values.

Example: If after swapping, basket1 = [1, 2, 3] and basket2 = [3, 1, 2], they are equal because when sorted, both become [1, 2, 3].

Your task: Find the minimum total cost to make both baskets equal through swapping. If it's impossible to make them equal, return -1.

Key Insights:

  • You need to redistribute the fruits so both baskets have the same multiset of values
  • Each fruit value must appear an even number of times in total (across both baskets) for equal distribution to be possible
  • The swapping cost depends on choosing which fruits to swap - you want to minimize the total cost
Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

Let's think about what needs to happen for both baskets to become equal. If we count how many times each fruit value appears across both baskets, we need to redistribute them so each basket gets exactly half of each fruit type.

For example, if the value 5 appears 4 times total (2 in basket1, 2 in basket2), we're already balanced for that value. But if 3 appears 4 times total with 3 in basket1 and 1 in basket2, we need to move one 3 from basket1 to basket2.

This leads to our first key insight: For any fruit value, if it appears an odd number of times total, it's impossible to split evenly - we'd return -1.

Now, how do we track what needs to be moved? We can use a counter that tracks the "imbalance" for each fruit value. If basket1 has excess of a fruit, we mark it positive; if basket2 has excess, we mark it negative. The fruits with non-zero counts need to be swapped.

For the swapping strategy, we have two options:

  1. Direct swap: Pick a fruit from basket1 that needs to move and swap it with a fruit from basket2 that needs to move. The cost is the minimum of the two fruit values.
  2. Indirect swap using a middleman: Sometimes it's cheaper to use the globally cheapest fruit as an intermediary. We swap the fruit we want to move with the cheapest fruit (cost = fruit value), then swap that cheap fruit to the other basket (cost = cheap fruit value). Total cost = 2 * cheapest_fruit_value.

Here's the clever part: when we have a list of all fruits that need to be moved (half from each basket to the other), we want to pair them optimally. If we sort all the excess fruits that need swapping, we can pair the smallest with the largest, second smallest with second largest, and so on. But for each pair, we also consider if using the middleman (cheapest fruit overall) would be cheaper.

The final insight: we only need to process half of the fruits that need swapping because each swap fixes the position of two fruits simultaneously - one moves from basket1 to basket2, and another moves from basket2 to basket1.

Learn more about Greedy patterns.

Solution Approach

Let's walk through the implementation step by step:

Step 1: Count the imbalance We use a Counter to track which fruits need to be moved. For each position i, we increment the counter for basket1[i] and decrement for basket2[i]. This gives us the net difference for each fruit value.

cnt = Counter()
for a, b in zip(basket1, basket2):
    cnt[a] += 1
    cnt[b] -= 1

After this, if cnt[x] = 4, it means fruit x appears 4 more times in basket1 than needed, so we need to move 2 of them to basket2.

Step 2: Check feasibility and find minimum fruit value While iterating through the counter, we:

  • Check if any fruit has an odd count difference (impossible to balance)
  • Track the minimum fruit value mi (for potential use as middleman)

Step 3: Build the list of fruits to swap For each fruit with imbalance, we add half of the excess fruits to our swap list:

nums = []
for x, v in cnt.items():
    if v % 2:
        return -1  # Odd count means impossible to balance
    nums.extend([x] * (abs(v) // 2))

If fruit x has count +4, we add two xs to nums (need to move 2 out of basket1). If fruit y has count -4, we also add two ys to nums (need to move 2 into basket1).

Step 4: Sort and calculate minimum cost We sort nums to optimally pair fruits for swapping:

nums.sort()
m = len(nums) // 2

We only process the first half because:

  • The first half represents the cheapest fruits to move
  • Each swap involves two fruits (one from each basket), so processing half covers all swaps

Step 5: Calculate the cost For each fruit in the first half, we choose the cheaper option:

  • Direct swap cost: the fruit's value x
  • Indirect swap through middleman: 2 * mi
return sum(min(x, mi * 2) for x in nums[:m])

Why this works:

  • Sorting ensures we're always choosing the cheapest possible swaps
  • The middleman strategy (2 * mi) is beneficial when the fruit we want to swap is expensive
  • By processing only the first half of sorted nums, we automatically pair cheap fruits with expensive ones, minimizing total cost

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through a concrete example to illustrate the solution approach.

Given:

  • basket1 = [4, 2, 2, 2]
  • basket2 = [1, 4, 1, 2]

Step 1: Count the imbalance

We create a counter tracking the difference for each fruit:

  • Position 0: cnt[4] += 1, cnt[1] -= 1
  • Position 1: cnt[2] += 1, cnt[4] -= 1
  • Position 2: cnt[2] += 1, cnt[1] -= 1
  • Position 3: cnt[2] += 1, cnt[2] -= 1

Result: cnt = {4: 0, 1: -2, 2: +2}

This means:

  • Fruit 4 is balanced (appears equally in both baskets)
  • Fruit 1 has deficit of 2 in basket1 (basket2 has 2 extra)
  • Fruit 2 has excess of 2 in basket1 (basket1 has 2 extra)

Step 2: Check feasibility and find minimum

All counts are even (0, -2, +2), so it's possible to balance. The minimum fruit value across both baskets is mi = 1.

Step 3: Build swap list

For each imbalanced fruit, add half the absolute count:

  • Fruit 1: |-2| // 2 = 1, add one 1 to nums
  • Fruit 2: |+2| // 2 = 1, add one 2 to nums

nums = [1, 2]

Step 4: Sort and prepare for cost calculation

nums = [1, 2] (already sorted) m = len(nums) // 2 = 1

We only need to process the first fruit (nums[:1] = [1])

Step 5: Calculate minimum cost

For the fruit with value 1:

  • Direct swap cost: 1
  • Indirect swap using middleman: 2 * mi = 2 * 1 = 2
  • Choose minimum: min(1, 2) = 1

Total cost: 1

What actually happens: We swap one 2 from basket1 with one 1 from basket2. The cost is min(2, 1) = 1.

After swapping:

  • basket1 = [4, 1, 2, 2] β†’ sorted: [1, 2, 2, 4]
  • basket2 = [2, 4, 1, 2] β†’ sorted: [1, 2, 2, 4]

Both baskets are now equal when sorted!

Solution Implementation

1class Solution:
2    def minCost(self, basket1: List[int], basket2: List[int]) -> int:
3        # Count the frequency difference between basket1 and basket2
4        # Positive count means excess in basket1, negative means excess in basket2
5        frequency_diff = Counter()
6        for fruit1, fruit2 in zip(basket1, basket2):
7            frequency_diff[fruit1] += 1
8            frequency_diff[fruit2] -= 1
9      
10        # Find the minimum value fruit across both baskets
11        # This will be used for indirect swapping optimization
12        min_fruit_value = min(frequency_diff)
13      
14        # Collect all fruits that need to be swapped
15        fruits_to_swap = []
16        for fruit_value, count_diff in frequency_diff.items():
17            # If the difference is odd, it's impossible to balance
18            if count_diff % 2:
19                return -1
20            # Add half of the excess fruits to the swap list
21            # We only need to swap half because each swap fixes two positions
22            fruits_to_swap.extend([fruit_value] * (abs(count_diff) // 2))
23      
24        # Sort fruits by value to minimize swap costs
25        fruits_to_swap.sort()
26      
27        # We only need to consider the first half of swaps
28        # The second half is implicitly handled by the first half
29        num_swaps_needed = len(fruits_to_swap) // 2
30      
31        # Calculate minimum cost by choosing between direct swap and indirect swap
32        # Direct swap: use the fruit value as cost
33        # Indirect swap: use minimum fruit twice (swap with min, then min with target)
34        return sum(min(fruit_value, min_fruit_value * 2) for fruit_value in fruits_to_swap[:num_swaps_needed])
35
1class Solution {
2    public long minCost(int[] basket1, int[] basket2) {
3        int n = basket1.length;
4      
5        // Track the frequency difference between basket1 and basket2
6        // Positive value means more in basket1, negative means more in basket2
7        Map<Integer, Integer> frequencyDifference = new HashMap<>();
8      
9        // Count occurrences: +1 for basket1, -1 for basket2
10        for (int i = 0; i < n; i++) {
11            frequencyDifference.merge(basket1[i], 1, Integer::sum);
12            frequencyDifference.merge(basket2[i], -1, Integer::sum);
13        }
14      
15        // Track the minimum value across both baskets (for indirect swapping strategy)
16        int minimumValue = Integer.MAX_VALUE;
17      
18        // List to store fruits that need to be swapped
19        List<Integer> fruitsToSwap = new ArrayList<>();
20      
21        // Process each fruit type
22        for (Map.Entry<Integer, Integer> entry : frequencyDifference.entrySet()) {
23            int fruitValue = entry.getKey();
24            int difference = entry.getValue();
25          
26            // If difference is odd, we cannot balance this fruit between baskets
27            if (difference % 2 != 0) {
28                return -1;
29            }
30          
31            // Add half of the absolute difference to swap list
32            // We only need to swap half because each swap fixes two positions
33            int swapsNeeded = Math.abs(difference) / 2;
34            for (int i = 0; i < swapsNeeded; i++) {
35                fruitsToSwap.add(fruitValue);
36            }
37          
38            // Update minimum value
39            minimumValue = Math.min(minimumValue, fruitValue);
40        }
41      
42        // Sort fruits by value to minimize cost
43        Collections.sort(fruitsToSwap);
44      
45        // Calculate minimum cost
46        // We only need to consider first half of sorted list (cheapest swaps)
47        int totalSwaps = fruitsToSwap.size();
48        long totalCost = 0;
49      
50        for (int i = 0; i < totalSwaps / 2; i++) {
51            // For each swap, we can either:
52            // 1. Direct swap with cost = fruitsToSwap.get(i)
53            // 2. Indirect swap using minimum value twice with cost = minimumValue * 2
54            totalCost += Math.min(fruitsToSwap.get(i), minimumValue * 2);
55        }
56      
57        return totalCost;
58    }
59}
60
1class Solution {
2public:
3    long long minCost(vector<int>& basket1, vector<int>& basket2) {
4        int n = basket1.size();
5      
6        // Count the frequency difference between basket1 and basket2
7        // Positive value means excess in basket1, negative means excess in basket2
8        unordered_map<int, int> frequencyDifference;
9        for (int i = 0; i < n; ++i) {
10            frequencyDifference[basket1[i]]++;
11            frequencyDifference[basket2[i]]--;
12        }
13      
14        // Track the minimum value across both baskets
15        int minValue = INT_MAX;
16      
17        // Store the values that need to be swapped
18        vector<int> valuesToSwap;
19      
20        // Process each unique value and its frequency difference
21        for (auto& [value, difference] : frequencyDifference) {
22            // If the difference is odd, it's impossible to balance the baskets
23            if (difference % 2 != 0) {
24                return -1;
25            }
26          
27            // Add half of the absolute difference to swap list
28            // This represents how many of this value need to be moved
29            int swapCount = abs(difference) / 2;
30            for (int i = 0; i < swapCount; ++i) {
31                valuesToSwap.push_back(value);
32            }
33          
34            // Update the minimum value found
35            minValue = min(minValue, value);
36        }
37      
38        // Sort values to swap in ascending order
39        sort(valuesToSwap.begin(), valuesToSwap.end());
40      
41        // Calculate minimum cost by swapping the first half of sorted values
42        // For each swap, we can either:
43        // 1. Swap directly (cost = smaller value)
44        // 2. Use the minimum value as intermediate (cost = 2 * minValue)
45        int totalSwaps = valuesToSwap.size();
46        long long totalCost = 0;
47      
48        for (int i = 0; i < totalSwaps / 2; ++i) {
49            totalCost += min(valuesToSwap[i], minValue * 2);
50        }
51      
52        return totalCost;
53    }
54};
55
1/**
2 * Finds the minimum cost to make two baskets equal by swapping fruits
3 * @param basket1 - First basket containing fruits (represented as numbers)
4 * @param basket2 - Second basket containing fruits (represented as numbers)
5 * @returns Minimum cost to make baskets equal, or -1 if impossible
6 */
7function minCost(basket1: number[], basket2: number[]): number {
8    const basketSize: number = basket1.length;
9  
10    // Track the difference in fruit counts between the two baskets
11    // Positive value means excess in basket1, negative means excess in basket2
12    const fruitCountDifference: Map<number, number> = new Map();
13  
14    // Calculate the net difference for each fruit type
15    for (let i = 0; i < basketSize; i++) {
16        fruitCountDifference.set(
17            basket1[i], 
18            (fruitCountDifference.get(basket1[i]) || 0) + 1
19        );
20        fruitCountDifference.set(
21            basket2[i], 
22            (fruitCountDifference.get(basket2[i]) || 0) - 1
23        );
24    }
25  
26    // Track the minimum fruit value across both baskets
27    let minimumFruitValue: number = Number.MAX_SAFE_INTEGER;
28  
29    // Collect fruits that need to be swapped
30    const fruitsToSwap: number[] = [];
31  
32    // Process each fruit type and its count difference
33    for (const [fruitValue, countDifference] of fruitCountDifference.entries()) {
34        // If count difference is odd, equal distribution is impossible
35        if (countDifference % 2 !== 0) {
36            return -1;
37        }
38      
39        // Add half of the excess fruits to the swap list
40        // We only need to swap half because each swap fixes two fruits
41        const swapsNeeded: number = Math.abs(countDifference) / 2;
42        for (let i = 0; i < swapsNeeded; i++) {
43            fruitsToSwap.push(fruitValue);
44        }
45      
46        // Update the minimum fruit value
47        minimumFruitValue = Math.min(minimumFruitValue, fruitValue);
48    }
49  
50    // Sort fruits to swap in ascending order to minimize cost
51    fruitsToSwap.sort((a, b) => a - b);
52  
53    const totalSwapsNeeded: number = fruitsToSwap.length;
54    let totalMinimumCost: number = 0;
55  
56    // Calculate minimum cost by choosing the cheaper option for each swap:
57    // 1. Direct swap using the current fruit (cost = fruit value)
58    // 2. Indirect swap using minimum fruit twice (cost = 2 * minimum value)
59    for (let i = 0; i < totalSwapsNeeded / 2; i++) {
60        totalMinimumCost += Math.min(fruitsToSwap[i], minimumFruitValue * 2);
61    }
62  
63    return totalMinimumCost;
64}
65

Time and Space Complexity

Time Complexity: O(n Γ— log n)

The time complexity is dominated by the following operations:

  • Creating the counter by iterating through both baskets: O(n) where n is the length of each basket
  • Finding the minimum value in the counter: O(k) where k is the number of unique elements
  • Building the nums list by iterating through counter items: O(k) for iteration, and the total elements added to nums is at most n
  • Sorting the nums list: O(m Γ— log m) where m is the length of nums, and in the worst case m = n
  • Computing the sum for the first half of sorted nums: O(m/2) = O(n)

Since the sorting operation O(n Γ— log n) dominates all other operations, the overall time complexity is O(n Γ— log n).

Space Complexity: O(n)

The space complexity comes from:

  • Counter dictionary cnt: O(k) where k is the number of unique elements, at most O(n)
  • List nums: Can contain at most n elements in the worst case when all swaps are needed
  • Other variables like mi and m: O(1)

Therefore, the overall space complexity is O(n).

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Misunderstanding the Swap Pairing Logic

Pitfall: A common mistake is thinking that we need to explicitly pair fruits from basket1 with fruits from basket2 when calculating costs. Developers might try to track which specific fruits are being swapped between baskets.

Why it happens: The problem statement mentions swapping fruit i from basket1 with fruit j from basket2, leading to overthinking about which exact fruits to pair.

The Issue:

# INCORRECT approach - trying to track specific swaps
basket1_excess = []
basket2_excess = []
for fruit, count in frequency_diff.items():
    if count > 0:
        basket1_excess.extend([fruit] * (count // 2))
    else:
        basket2_excess.extend([fruit] * (-count // 2))
# Then trying to pair them somehow...

Solution: The key insight is that after collecting all fruits that need swapping and sorting them, we only need to consider the first half. Each fruit in the first half implicitly pairs with a corresponding fruit in the second half, and we only need to count the cost once per swap (using the minimum of the two fruits being swapped).

2. Incorrect Minimum Value Calculation

Pitfall: Using min(basket1 + basket2) to find the minimum fruit value instead of min(frequency_diff).

Why it happens: It seems logical to find the minimum across all original fruits.

The Issue:

# INCORRECT
min_fruit_value = min(basket1 + basket2)  # Wrong!

# CORRECT
min_fruit_value = min(frequency_diff)  # Uses keys from the counter

Solution: The min(frequency_diff) correctly gets the minimum fruit value that appears in either basket because frequency_diff contains all unique fruit values as keys. Using min(basket1 + basket2) would work but is less efficient.

3. Forgetting the Indirect Swap Optimization

Pitfall: Only considering direct swaps and missing the optimization of using a cheap fruit as a middleman.

The Issue:

# INCORRECT - only considering direct swaps
return sum(fruits_to_swap[:num_swaps_needed])  # Missing optimization!

Why it matters: If you have expensive fruits to swap (e.g., cost 100) but there's a cheap fruit (e.g., cost 1), it's cheaper to do two swaps through the cheap fruit (cost 2) than one direct swap (cost 100).

Solution: Always compare the direct swap cost with the indirect swap cost:

return sum(min(fruit_value, min_fruit_value * 2) for fruit_value in fruits_to_swap[:num_swaps_needed])

4. Processing the Entire Swap List Instead of Half

Pitfall: Summing costs for all fruits in fruits_to_swap instead of just the first half.

The Issue:

# INCORRECT - counts each swap twice
return sum(min(x, min_fruit_value * 2) for x in fruits_to_swap)  # Wrong!

Why it happens: Each swap involves two fruits, but we add both fruits to our list. If we process all fruits, we're essentially counting each swap twice.

Solution: Only process the first half after sorting:

num_swaps_needed = len(fruits_to_swap) // 2
return sum(min(fruit_value, min_fruit_value * 2) for fruit_value in fruits_to_swap[:num_swaps_needed])

This ensures we count each swap exactly once, using the cheaper fruit from each swap pair.

Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

In a binary min heap, the minimum element can be found in:


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More