sorting algorithm
data structure
equal values separation
algorithm design
computer science

Sorting algorithm to keep equal values separated

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

If the goal is to "sort" while also keeping equal values apart, the first thing to clarify is that those two requirements can conflict. A fully sorted array naturally groups duplicates together, so the real problem is usually a rearrangement problem: produce an ordered or near-ordered sequence where identical items are separated as much as possible.

Why Ordinary Sorting Is Not Enough

A normal sort places equal values next to each other:

[1, 1, 1, 2, 2, 3]

That is correct sorting, but it does the opposite of separating duplicates. If your real requirement is "no equal neighbors if possible," you need a different algorithm.

This is similar to scheduling or reorganizing characters in a string: always place the most frequent remaining value, but avoid placing the same value twice in a row.

A Greedy Heap-Based Solution

The standard greedy strategy is:

  1. count how many times each value appears
  2. keep the counts in a max heap
  3. pick the most frequent value that is not the same as the one just placed

That produces a sequence with duplicates spread out as much as possible.

python
1from collections import Counter
2import heapq
3
4
5def separate_equals(values):
6    counts = Counter(values)
7    heap = [(-count, value) for value, count in counts.items()]
8    heapq.heapify(heap)
9
10    result = []
11    prev = (0, None)
12
13    while heap:
14        count, value = heapq.heappop(heap)
15        result.append(value)
16        count += 1
17
18        if prev[0] < 0:
19            heapq.heappush(heap, prev)
20
21        prev = (count, value)
22
23    if len(result) != len(values):
24        raise ValueError("Cannot separate all equal values")
25
26    return result
27
28
29print(separate_equals([1, 1, 1, 2, 2, 3]))
30print(separate_equals([4, 4, 4, 5, 5, 6, 6]))

This does not produce a numerically sorted result. It produces a duplicate-separated result.

When Separation Is Impossible

If one value appears too often, avoiding adjacent duplicates is impossible. For example:

[1, 1, 1, 1, 2, 3]

There are not enough other values to keep all the 1 values apart.

A useful test is:

  • let max_count be the highest frequency
  • let n be the total length

If max_count > (n + 1) / 2, then complete separation is impossible.

You can still use the greedy algorithm to minimize clustering, but you should not promise a perfect arrangement in cases like that.

If You Need Partial Ordering Too

Some problems want both:

  • values generally ordered
  • duplicates not packed tightly

That becomes an optimization problem rather than a true sort. One practical approach is:

  1. group equal values
  2. interleave groups by frequency
  3. accept that perfect numeric order will be sacrificed

For example, a round-robin distribution across buckets can work well for display or scheduling tasks:

python
1from collections import defaultdict
2
3
4def bucket_spread(values):
5    groups = defaultdict(list)
6    for value in sorted(values):
7        groups[value].append(value)
8
9    buckets = []
10    while any(groups.values()):
11        row = []
12        for key in sorted(groups):
13            if groups[key]:
14                row.append(groups[key].pop())
15        buckets.extend(row)
16
17    return buckets
18
19
20print(bucket_spread([1, 1, 1, 2, 2, 3, 3]))

This preserves some ordering intuition without pretending the result is a standard sorted sequence.

Common Pitfalls

The biggest mistake is asking for a fully sorted output and fully separated duplicates at the same time without noticing the contradiction. In an ordinary ascending sort, duplicates belong together by definition.

Another issue is not checking feasibility. If one value dominates the input, no algorithm can prevent all adjacent duplicates.

A third pitfall is using repeated swap heuristics after sorting. Those approaches can work on small examples but often break down or become hard to reason about compared with a frequency-based greedy method.

Finally, be clear about the objective: exact sorting, no adjacent equals, or "spread duplicates as much as possible." Those are different problems and need different success criteria.

Summary

  • Standard sorting does not separate duplicates; it groups them.
  • If you want equal values apart, treat the task as a rearrangement problem.
  • A max-heap greedy algorithm is a common and effective solution.
  • Perfect separation is impossible when one value appears too frequently.
  • Define the real goal clearly before choosing the algorithm.

Course illustration
Course illustration

All Rights Reserved.