Facebook Pixel

1054. Distant Barcodes

Problem Description

You have a row of barcodes in a warehouse, where each barcode is represented by a number in the array barcodes[i].

Your task is to rearrange these barcodes so that no two adjacent barcodes have the same value. You need to return any valid arrangement, and the problem guarantees that at least one valid arrangement exists.

For example, if you have barcodes [1, 1, 1, 2, 2, 2], a valid rearrangement could be [1, 2, 1, 2, 1, 2] where no two adjacent elements are the same.

The solution works by:

  1. Counting the frequency of each barcode value using a Counter
  2. Sorting the barcodes by their frequency (highest frequency first), with ties broken by the barcode value itself
  3. Placing the sorted barcodes into the result array by filling even indices first (0, 2, 4, ...), then odd indices (1, 3, 5, ...)

This placement strategy ensures that the most frequent elements are spread out maximally. By filling even positions first with the first half of sorted elements, then odd positions with the second half, we guarantee that identical values (which are grouped together after sorting) will never be adjacent in the final arrangement.

The key insight is that since a solution is guaranteed to exist, the most frequent element appears at most (n + 1) // 2 times, which means it can be placed in all even positions without creating adjacencies.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The key observation is that we need to separate identical barcodes as far apart as possible. Think about the most frequent barcode - it poses the biggest challenge because we have many copies of it to place.

If a barcode appears k times, we need at least k-1 other barcodes to separate them. For the arrangement to be possible, the most frequent barcode can appear at most (n+1)//2 times (roughly half the total). Since the problem guarantees a solution exists, we know this condition is met.

The natural strategy is to start with the most frequent elements because they're the hardest to place. If we can successfully place them, the less frequent ones will be easier to handle.

Now, how do we maximize separation? Imagine the array positions as two groups: even indices [0, 2, 4, ...] and odd indices [1, 3, 5, ...]. If we place elements in one group first, then the other, we ensure maximum separation between identical elements.

By sorting barcodes by frequency (highest first) and grouping identical values together, we create a sequence where the most problematic elements come first. When we place the first half of this sorted sequence in even positions and the second half in odd positions, identical barcodes (which are grouped together in the sorted array) end up in completely different position groups, guaranteeing they won't be adjacent.

This approach works because:

  • The most frequent element fills at most all even positions (or all odd positions)
  • Elements of the same value are consecutive in the sorted array
  • Splitting the sorted array in half and placing halves in alternating positions ensures identical values never meet

Learn more about Greedy, Sorting and Heap (Priority Queue) patterns.

Solution Approach

Let's walk through the implementation step by step:

Step 1: Count Frequencies

cnt = Counter(barcodes)

We use Python's Counter to create a frequency map where each unique barcode value maps to its occurrence count. This helps us identify which barcodes appear most frequently.

Step 2: Sort by Frequency

barcodes.sort(key=lambda x: (-cnt[x], x))

We sort the entire barcodes array using a custom key:

  • Primary sort: -cnt[x] sorts by frequency in descending order (negative sign for descending)
  • Secondary sort: x sorts by the barcode value itself in ascending order (for tie-breaking)

After this sort, identical barcodes are grouped together, with the most frequent groups appearing first. For example, if barcode 1 appears 3 times and barcode 2 appears 2 times, the sorted array might look like [1, 1, 1, 2, 2, ...].

Step 3: Initialize Result Array

n = len(barcodes)
ans = [0] * len(barcodes)

We create an answer array of the same length as the input, initialized with zeros.

Step 4: Fill Even Positions

ans[::2] = barcodes[: (n + 1) // 2]

Using Python's slice notation [::2], we fill all even indices (0, 2, 4, ...) with the first half of the sorted barcodes. The slice barcodes[: (n + 1) // 2] takes the first (n + 1) // 2 elements.

Step 5: Fill Odd Positions

ans[1::2] = barcodes[(n + 1) // 2 :]

Using slice notation [1::2], we fill all odd indices (1, 3, 5, ...) with the remaining elements from the sorted array, starting from index (n + 1) // 2.

The beauty of this approach is that by splitting the sorted array at the midpoint and placing the two halves in alternating positions, we guarantee that identical values (which are consecutive in the sorted array) end up far apart in the final arrangement. Since the most frequent element appears at most (n + 1) // 2 times, it fits perfectly into either all even or all odd positions without creating adjacencies.

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 with barcodes = [1, 1, 1, 1, 2, 2, 3].

Step 1: Count Frequencies

  • Barcode 1 appears 4 times
  • Barcode 2 appears 2 times
  • Barcode 3 appears 1 time
  • cnt = {1: 4, 2: 2, 3: 1}

Step 2: Sort by Frequency We sort the array by frequency (descending), then by value (ascending for ties):

  • Original: [1, 1, 1, 1, 2, 2, 3]
  • After sorting: [1, 1, 1, 1, 2, 2, 3]
    • All 1's come first (frequency 4)
    • Then 2's (frequency 2)
    • Finally 3 (frequency 1)

Step 3: Prepare for Placement

  • Array length n = 7
  • Split point: (n + 1) // 2 = (7 + 1) // 2 = 4
  • First half: [1, 1, 1, 1] (indices 0-3 of sorted array)
  • Second half: [2, 2, 3] (indices 4-6 of sorted array)

Step 4: Fill Even Positions Place first half in even indices (0, 2, 4, 6):

  • Position 0: 1
  • Position 2: 1
  • Position 4: 1
  • Position 6: 1
  • Result so far: [1, _, 1, _, 1, _, 1]

Step 5: Fill Odd Positions Place second half in odd indices (1, 3, 5):

  • Position 1: 2
  • Position 3: 2
  • Position 5: 3
  • Final result: [1, 2, 1, 2, 1, 3, 1]

Verification: Checking adjacent pairs:

  • (1, 2) ✓ Different
  • (2, 1) ✓ Different
  • (1, 2) ✓ Different
  • (2, 1) ✓ Different
  • (1, 3) ✓ Different
  • (3, 1) ✓ Different

The algorithm successfully separated all identical barcodes! The most frequent element (1) was spread across all even positions, while the remaining elements filled odd positions, ensuring no adjacent duplicates.

Solution Implementation

1from typing import List
2from collections import Counter
3
4class Solution:
5    def rearrangeBarcodes(self, barcodes: List[int]) -> List[int]:
6        # Count frequency of each barcode
7        frequency_map = Counter(barcodes)
8      
9        # Sort barcodes by frequency (descending) and value (ascending as tiebreaker)
10        # This ensures most frequent elements are placed first
11        barcodes.sort(key=lambda barcode: (-frequency_map[barcode], barcode))
12      
13        # Get the length of the array
14        n = len(barcodes)
15      
16        # Initialize result array with zeros
17        result = [0] * n
18      
19        # Place first half of sorted barcodes at even indices (0, 2, 4, ...)
20        # This ensures the most frequent element is spread out maximally
21        result[::2] = barcodes[:(n + 1) // 2]
22      
23        # Place second half of sorted barcodes at odd indices (1, 3, 5, ...)
24        result[1::2] = barcodes[(n + 1) // 2:]
25      
26        return result
27
1class Solution {
2    public int[] rearrangeBarcodes(int[] barcodes) {
3        int length = barcodes.length;
4      
5        // Create an Integer array to store barcode values for custom sorting
6        Integer[] barcodeCopy = new Integer[length];
7        int maxBarcode = 0;
8      
9        // Copy barcodes to Integer array and find the maximum barcode value
10        for (int i = 0; i < length; ++i) {
11            barcodeCopy[i] = barcodes[i];
12            maxBarcode = Math.max(maxBarcode, barcodes[i]);
13        }
14      
15        // Count frequency of each barcode value
16        int[] frequency = new int[maxBarcode + 1];
17        for (int barcode : barcodes) {
18            ++frequency[barcode];
19        }
20      
21        // Sort barcodes by frequency (descending), then by value (ascending) for ties
22        Arrays.sort(barcodeCopy, (a, b) -> 
23            frequency[a] == frequency[b] ? a - b : frequency[b] - frequency[a]
24        );
25      
26        // Rearrange barcodes by placing them at even indices first, then odd indices
27        int[] result = new int[length];
28        int index = 0;
29      
30        // Fill even positions (0, 2, 4, ...) first
31        for (int position = 0; position < length; position += 2) {
32            result[position] = barcodeCopy[index++];
33        }
34      
35        // Fill odd positions (1, 3, 5, ...) next
36        for (int position = 1; position < length; position += 2) {
37            result[position] = barcodeCopy[index++];
38        }
39      
40        return result;
41    }
42}
43
1class Solution {
2public:
3    vector<int> rearrangeBarcodes(vector<int>& barcodes) {
4        // Find the maximum value in barcodes to determine array size
5        int maxValue = *max_element(barcodes.begin(), barcodes.end());
6      
7        // Create frequency array to count occurrences of each barcode
8        int frequency[maxValue + 1];
9        memset(frequency, 0, sizeof(frequency));
10      
11        // Count frequency of each barcode
12        for (int barcode : barcodes) {
13            ++frequency[barcode];
14        }
15      
16        // Sort barcodes by frequency (descending) and value (ascending) as tiebreaker
17        // This ensures most frequent elements are placed first
18        sort(barcodes.begin(), barcodes.end(), [&](int a, int b) {
19            if (frequency[a] != frequency[b]) {
20                return frequency[a] > frequency[b];
21            }
22            return a < b;
23        });
24      
25        int n = barcodes.size();
26        vector<int> result(n);
27      
28        // Place barcodes in alternating positions to avoid adjacent duplicates
29        // First fill even indices (0, 2, 4, ...), then odd indices (1, 3, 5, ...)
30        int barcodeIndex = 0;
31      
32        // Fill even positions first
33        for (int i = 0; i < n; i += 2) {
34            result[i] = barcodes[barcodeIndex++];
35        }
36      
37        // Fill odd positions
38        for (int i = 1; i < n; i += 2) {
39            result[i] = barcodes[barcodeIndex++];
40        }
41      
42        return result;
43    }
44};
45
1/**
2 * Rearranges barcodes so that no two adjacent barcodes are the same
3 * @param barcodes - Array of barcode numbers to rearrange
4 * @returns Array with barcodes rearranged to avoid adjacent duplicates
5 */
6function rearrangeBarcodes(barcodes: number[]): number[] {
7    // Find the maximum value in barcodes to determine array size
8    const maxValue: number = Math.max(...barcodes);
9  
10    // Create frequency counter array for each barcode value
11    const frequency: number[] = Array(maxValue + 1).fill(0);
12  
13    // Count occurrences of each barcode
14    for (const barcode of barcodes) {
15        frequency[barcode]++;
16    }
17  
18    // Sort barcodes by frequency (descending), then by value (ascending) for ties
19    barcodes.sort((a: number, b: number) => {
20        if (frequency[a] === frequency[b]) {
21            return a - b;  // If same frequency, sort by value ascending
22        }
23        return frequency[b] - frequency[a];  // Sort by frequency descending
24    });
25  
26    // Get the total number of barcodes
27    const totalCount: number = barcodes.length;
28  
29    // Initialize result array
30    const result: number[] = Array(totalCount);
31  
32    // Place barcodes in result array using odd-even positioning strategy
33    // First fill even indices (0, 2, 4, ...), then odd indices (1, 3, 5, ...)
34    let barcodeIndex: number = 0;
35  
36    for (let pass = 0; pass < 2; pass++) {
37        // In first pass: fill even positions (starting from 0)
38        // In second pass: fill odd positions (starting from 1)
39        for (let position = pass; position < totalCount; position += 2) {
40            result[position] = barcodes[barcodeIndex];
41            barcodeIndex++;
42        }
43    }
44  
45    return result;
46}
47

Time and Space Complexity

Time Complexity: O(n × log n)

The time complexity is dominated by the sorting operation. Breaking down the algorithm:

  • Creating the Counter takes O(n) time
  • Sorting the barcodes array takes O(n × log n) time, where each comparison involves looking up the count in the Counter dictionary (which is O(1))
  • The slicing operations ans[::2] and ans[1::2] each take O(n) time
  • Overall time complexity: O(n) + O(n × log n) + O(n) = O(n × log n)

Space Complexity: O(n)

The space complexity analysis:

  • The Counter dictionary stores at most n unique elements, using O(n) space
  • The ans array uses O(n) space
  • The sorting operation may use O(n) auxiliary space depending on the implementation
  • Total space complexity: O(n)

Note: The reference answer mentions O(M) where M is the maximum value in the array. However, in this implementation, the space complexity is actually O(n) because the Counter stores unique values (at most n elements) rather than using an array indexed by barcode values up to M.

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

Common Pitfalls

1. Incorrect Index Calculation for Array Splitting

A common mistake is using n // 2 instead of (n + 1) // 2 when splitting the sorted array. This causes issues with odd-length arrays.

Why it's wrong:

  • For an array of length 5, using n // 2 gives us 2 elements for even positions, but we have 3 even positions (indices 0, 2, 4)
  • This leads to index out of bounds errors or incorrect placement

Correct approach:

# For odd-length arrays: (n + 1) // 2 gives the correct split point
# Length 5: (5 + 1) // 2 = 3 elements for positions [0, 2, 4]
# Length 6: (6 + 1) // 2 = 3 elements for positions [0, 2, 4]
result[::2] = barcodes[:(n + 1) // 2]
result[1::2] = barcodes[(n + 1) // 2:]

2. Forgetting to Handle the Most Frequent Element Properly

Some might try to use a greedy approach without ensuring the most frequent element is placed first, which can lead to invalid arrangements.

Why it matters:

  • If the most frequent element appears (n + 1) // 2 times, it MUST occupy all even positions
  • Placing it anywhere else might create adjacencies

Solution: Always sort by frequency first to ensure the most frequent elements are handled priority-wise.

3. Using a Priority Queue Without Proper Reconstruction

While using a max-heap/priority queue is a valid alternative approach, a pitfall is popping elements one by one and placing them sequentially, which doesn't guarantee non-adjacency.

Incorrect pattern:

# This doesn't work - sequential placement can create adjacencies
heap = [(-freq, val) for val, freq in frequency_map.items()]
heapq.heapify(heap)
result = []
while heap:
    freq, val = heapq.heappop(heap)
    result.append(val)  # Wrong: might place same values adjacent

Correct pattern: Use the alternating placement strategy or maintain two different elements at a time when using a heap approach.

4. Not Preserving All Occurrences After Sorting

A critical mistake is sorting unique values by frequency rather than sorting the entire array while maintaining all duplicates.

Wrong approach:

# This loses the actual count of elements
sorted_values = sorted(frequency_map.keys(), key=lambda x: -frequency_map[x])
# Now we don't have the right number of each element

Right approach:

# Sort the entire array, keeping all duplicates
barcodes.sort(key=lambda x: (-frequency_map[x], x))
# This groups identical values together while preserving count
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

How many times is a tree node visited in a depth first search?


Recommended Readings

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

Load More