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:
- Counting the frequency of each barcode value using a Counter
- Sorting the barcodes by their frequency (highest frequency first), with ties broken by the barcode value itself
- 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.
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 EvaluatorExample 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 isO(1)
) - The slicing operations
ans[::2]
andans[1::2]
each takeO(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, usingO(n)
space - The
ans
array usesO(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
How many times is a tree node visited in a depth first search?
Recommended Readings
Greedy Introduction div class responsive iframe iframe src https www youtube com embed WTslqPbj7I title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture web share allowfullscreen iframe div When do we use greedy Greedy algorithms tend to solve optimization problems Typically they will ask you to calculate the max min of some value Commonly you may see this phrased in the problem as max min longest shortest largest smallest etc These keywords can be identified by just scanning
Sorting Summary Comparisons We presented quite a few sorting algorithms and it is essential to know the advantages and disadvantages of each one The basic algorithms are easy to visualize and easy to learn for beginner programmers because of their simplicity As such they will suffice if you don't know any advanced
https assets algo monster cover_photos heap svg Priority Queue and Heap What is the relationship between priority queue and heap Priority Queue is an Abstract Data Type and Heap is the concrete data structure we use to implement a priority queue Priority Queue A priority queue is a data structure
Want a Structured Path to Master System Design Too? Don’t Miss This!