Facebook Pixel

3046. Split the Array

EasyArrayHash TableCounting
Leetcode Link

Problem Description

You have an integer array nums with an even number of elements. Your task is to split this array into two equal parts, nums1 and nums2, where each part must satisfy specific conditions.

The requirements are:

  • Both nums1 and nums2 must have exactly half the elements of the original array (i.e., nums1.length == nums2.length == nums.length / 2)
  • All elements within nums1 must be distinct (no duplicates allowed)
  • All elements within nums2 must be distinct (no duplicates allowed)

You need to determine if such a split is possible and return true if it can be done, or false otherwise.

The key insight is that if any element appears 3 or more times in the original array, it's impossible to split the array while maintaining distinct elements in both parts. This is because if an element appears 3 times, at least one of the two parts would need to contain that element twice, violating the distinctness requirement.

For example:

  • If nums = [1, 1, 2, 2, 3, 4], you can split it into [1, 2, 3] and [1, 2, 4], so return true
  • If nums = [1, 1, 1, 1], no valid split exists since the element 1 appears 4 times, so return false
Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

Let's think about what happens when we try to split the array. Since both resulting arrays must contain only distinct elements, we need to carefully distribute any duplicate values that exist in the original array.

If an element appears once in nums, we can place it in either nums1 or nums2 - no problem there.

If an element appears twice in nums, we must place one copy in nums1 and one copy in nums2 to maintain distinctness in both arrays.

But what if an element appears three times or more? Here's where we hit a wall. We have only two arrays to distribute these copies into, and each array can accept at most one copy (to maintain distinctness). So with 3 or more copies of the same element, at least one would be left without a valid placement.

This leads us to the critical realization: the maximum frequency of any element in the array determines whether a valid split is possible. If any element appears 3 or more times, we cannot split the array while satisfying all constraints.

Therefore, we simply need to count the frequency of each element in the array. If the maximum frequency is less than 3 (meaning all elements appear at most twice), we can successfully split the array. Otherwise, it's impossible.

This explains why the solution max(Counter(nums).values()) < 3 works perfectly - it directly checks if the highest frequency among all elements is less than 3.

Solution Approach

The solution uses a counting approach to determine if the array can be split according to the requirements.

Implementation Steps:

  1. Count Element Frequencies: Use Python's Counter from the collections module to count how many times each element appears in the array. Counter(nums) creates a dictionary-like object where keys are the unique elements and values are their frequencies.

  2. Extract Maximum Frequency: Get all frequency values using .values() method, which returns a view of all the counts. Then find the maximum frequency using the max() function.

  3. Check the Condition: Compare if the maximum frequency is less than 3. If it is, return true (the array can be split); otherwise, return false.

Why This Works:

  • When all elements appear at most twice, we can distribute them between nums1 and nums2:

    • Elements appearing once can go to either array
    • Elements appearing twice must have one copy in each array
  • When any element appears 3 or more times, we cannot place all copies while maintaining distinctness in both arrays (we only have 2 arrays but need to place 3+ copies)

Time and Space Complexity:

  • Time Complexity: O(n) where n is the length of the input array, as we need to iterate through the array once to count frequencies
  • Space Complexity: O(k) where k is the number of unique elements in the array, needed to store the frequency counter

The elegance of this solution lies in its simplicity - rather than trying to actually construct the split, we identify the necessary and sufficient condition (max frequency < 3) that determines whether a valid split exists.

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 the solution with a concrete example: nums = [3, 5, 3, 2, 5, 2, 1, 1]

Step 1: Count Element Frequencies Using Counter(nums), we get:

  • Element 3 appears 2 times
  • Element 5 appears 2 times
  • Element 2 appears 2 times
  • Element 1 appears 2 times

So our frequency map is: {3: 2, 5: 2, 2: 2, 1: 2}

Step 2: Find Maximum Frequency Looking at all the frequencies: [2, 2, 2, 2] The maximum frequency is 2

Step 3: Check if Maximum Frequency < 3 Since 2 < 3, the condition is satisfied, so we return true.

Verification: We can indeed split this array into two valid parts:

  • nums1 = [3, 5, 2, 1] (all distinct)
  • nums2 = [3, 5, 2, 1] (all distinct)

Each element that appeared twice in the original array has one copy in each resulting array, satisfying all constraints.


Now let's consider a failing case: nums = [4, 4, 4, 2]

Step 1: Count Element Frequencies

  • Element 4 appears 3 times
  • Element 2 appears 1 time

Frequency map: {4: 3, 2: 1}

Step 2: Find Maximum Frequency Looking at frequencies: [3, 1] The maximum frequency is 3

Step 3: Check if Maximum Frequency < 3 Since 3 < 3 is false, we return false.

Why it fails: Element 4 appears 3 times, but we can only place at most one copy in each of our two arrays. The third copy has nowhere to go without creating a duplicate, making a valid split impossible.

Solution Implementation

1from typing import List
2from collections import Counter
3
4class Solution:
5    def isPossibleToSplit(self, nums: List[int]) -> bool:
6        # Count the frequency of each number in the array
7        frequency_map = Counter(nums)
8      
9        # Get the maximum frequency among all numbers
10        max_frequency = max(frequency_map.values())
11      
12        # Check if the maximum frequency is less than 3
13        # If any number appears 3 or more times, we cannot split
14        # the array into two parts where each part has distinct elements
15        return max_frequency < 3
16
1class Solution {
2    /**
3     * Determines if an array can be split into two arrays where each element appears at most once in each array.
4     * This is possible only if no element appears more than twice in the original array.
5     * 
6     * @param nums the input array of integers (values range from 1 to 100)
7     * @return true if the array can be split as described, false otherwise
8     */
9    public boolean isPossibleToSplit(int[] nums) {
10        // Create a frequency counter array for values 1-100
11        // Index represents the number, value represents its frequency
12        int[] frequencyCounter = new int[101];
13      
14        // Count the frequency of each number in the input array
15        for (int number : nums) {
16            // Increment the count for the current number
17            frequencyCounter[number]++;
18          
19            // If any number appears 3 or more times, splitting is impossible
20            // (can't split into two arrays with each element appearing at most once)
21            if (frequencyCounter[number] >= 3) {
22                return false;
23            }
24        }
25      
26        // All numbers appear at most twice, splitting is possible
27        return true;
28    }
29}
30
1class Solution {
2public:
3    bool isPossibleToSplit(vector<int>& nums) {
4        // Array to count frequency of each number (constraint: numbers are in range [1, 100])
5        int frequency[101] = {0};
6      
7        // Iterate through all numbers in the input array
8        for (int num : nums) {
9            // Increment the frequency count for current number
10            frequency[num]++;
11          
12            // If any number appears 3 or more times, we cannot split into two arrays
13            // with distinct elements
14            if (frequency[num] >= 3) {
15                return false;
16            }
17        }
18      
19        // All numbers appear at most twice, so splitting is possible
20        return true;
21    }
22};
23
1/**
2 * Checks if it's possible to split the array into two arrays where each element appears at most twice
3 * @param nums - Input array of numbers
4 * @returns true if the array can be split, false otherwise
5 */
6function isPossibleToSplit(nums: number[]): boolean {
7    // Initialize frequency counter array for values 0-100
8    const frequencyCounter: number[] = Array(101).fill(0);
9  
10    // Count frequency of each number in the input array
11    for (const num of nums) {
12        // Increment counter and check if any number appears 3 or more times
13        frequencyCounter[num]++;
14      
15        // If any number appears 3 or more times, splitting is impossible
16        if (frequencyCounter[num] >= 3) {
17            return false;
18        }
19    }
20  
21    // All numbers appear at most twice, splitting is possible
22    return true;
23}
24

Time and Space Complexity

The time complexity is O(n), where n is the length of the array nums. This is because:

  • Counter(nums) iterates through all elements in the array once to count the frequency of each element, which takes O(n) time
  • .values() returns a view of the counter values in O(1) time
  • max() iterates through all the frequency values to find the maximum, which in the worst case is O(n) when all elements are unique, but typically O(k) where k is the number of distinct elements (k ≤ n)
  • The overall time complexity is dominated by the Counter construction, resulting in O(n)

The space complexity is O(n), where n is the length of the array. This is because:

  • Counter(nums) creates a dictionary that stores the frequency of each distinct element
  • In the worst case, when all elements are unique, the Counter will store n key-value pairs
  • Therefore, the space complexity is O(n)

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

Common Pitfalls

1. Empty Array Handling

The current solution will crash with a ValueError when given an empty array because max() cannot operate on an empty sequence.

Problem Example:

nums = []
# max(frequency_map.values()) will raise ValueError: max() arg is an empty sequence

Solution:

def isPossibleToSplit(self, nums: List[int]) -> bool:
    if not nums:  # Handle empty array
        return True  # An empty array can trivially be split into two empty arrays
  
    frequency_map = Counter(nums)
    max_frequency = max(frequency_map.values())
    return max_frequency < 3

2. Odd-Length Array Assumption

While the problem states the array has an even number of elements, defensive programming suggests validating this assumption to avoid silent failures.

Problem Example:

nums = [1, 2, 3]  # Odd length - cannot be split into two equal parts

Solution:

def isPossibleToSplit(self, nums: List[int]) -> bool:
    if len(nums) % 2 != 0:  # Validate even length
        return False
  
    frequency_map = Counter(nums)
    max_frequency = max(frequency_map.values())
    return max_frequency < 3

3. Overlooking Distribution Constraints

A subtle pitfall is not recognizing that even with max frequency < 3, we need enough unique elements to fill both arrays. If we have too many duplicate pairs relative to unique elements, the split might still be impossible.

Problem Example:

nums = [1, 1, 2, 2]  # This works fine
nums = [1, 1, 2, 2, 3, 3, 4, 4]  # This also works
# But conceptually, if most elements appear twice and very few appear once,
# we might run into distribution issues

Enhanced Solution with Additional Validation:

def isPossibleToSplit(self, nums: List[int]) -> bool:
    if not nums or len(nums) % 2 != 0:
        return False if nums else True
  
    frequency_map = Counter(nums)
  
    # Check max frequency condition
    if max(frequency_map.values()) >= 3:
        return False
  
    # Additional check: ensure we have enough unique elements
    # to fill both arrays (each needs n/2 elements)
    ones = sum(1 for freq in frequency_map.values() if freq == 1)
    twos = sum(1 for freq in frequency_map.values() if freq == 2)
  
    # Each array needs exactly n/2 elements
    # Elements with frequency 2 contribute 1 to each array
    # Elements with frequency 1 can go to either array
    required_per_array = len(nums) // 2
  
    # We need at least required_per_array unique elements total
    return len(frequency_map) >= required_per_array

4. Integer Overflow in Other Languages

While not an issue in Python, if implementing in languages like C++ or Java, be mindful of potential integer overflow when dealing with very large arrays or frequency counts.

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

The three-steps of Depth First Search are:

  1. Identify states;
  2. Draw the state-space tree;
  3. DFS on the state-space tree.

Recommended Readings

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

Load More