K-Distribution Sort applied to the push-swap problem
Part I — Push-Swap: A Minimalist Sorting Challenge Using Two Stacks
K-Distribution Sort applied to the push-swap problem
Part I — Push-Swap: A Minimalist Sorting Challenge Using Two Stacks
The goal of this article is to present a sorting algorithm for the push-swap problem that is clear, simple to understand, and easy to implement, while already delivering strong native performance without relying on advanced optimizations. The proposed method focuses on algorithmic clarity and structural elegance, resulting in very compact code that remains efficient on large input sizes.
Purpose
The push-swap project is a sorting challenge that requires sorting a list of integers using only two stacks and a restricted set of operations. The goal is to generate the shortest possible sequence of instructions to sort the initial stack, under tight algorithmic and structural constraints. Despite its simple rule set, push-swap demands efficient strategies, as naive approaches quickly lead to suboptimal results.

Push-swap purpose
Primitives
The operations in push-swap are based on fundamental stack behaviors:
**push**: places an element on top of a stack.**pop**: removes the top element from a stack.**swap**: exchanges the two elements at the top.**rotate**: shifts all elements upward by one position; the top moves to the bottom.**reverse rotate**: shifts all elements downward by one; the bottom moves to the top.
These primitives form the entire vocabulary for transforming an unsorted stack into a sorted one under strict constraints.

Stack primitives
Allowed operations
The sorting must be performed using only the following predefined operations:
Swap
sa: swap the top two elements of stack Asb: swap the top two elements of stack Bss: perform bothsaandsbsimultaneously
Push
pa: pop the top element from stack B and push it onto stack Apb: pop the top element from stack A and push it onto stack B
Rotate (upward)
ra: shift all elements of stack A up by one (top becomes bottom)rb: same for stack Brr: perform bothraandrbsimultaneously
Reverse Rotate (downward)
rra: shift all elements of stack A down by one (bottom becomes top)rrb: same for stack Brrr: perform bothrraandrrbsimultaneously
Each operation counts as a single instruction and must be printed exactly as shown to standard output.

Part II — The K-Distribution Sort
Overview:
The K-Distribution Sort is a heuristic algorithm tailored for the push-swap sorting problem. It operates by iteratively pushing elements from stack A to stack B, using a growing threshold to decide which elements to transfer and how to place them within B. The technique is particularly known for shaping stack B into a "K-like" structure when visualized—hence the name.
Mechanism:
Let index(i) be the normalized index of each element in stack A (from 0 to n-1).
We define a dynamic threshold window controlled by a parameter delta:
threshold = 0;
while A is not empty:
if A.head.index <= threshold + delta:
push A → B
if A.head.index <= threshold:
rotate B
threshold++
else:
rotate A
- Elements with
index ≤ thresholdare pushed then rotated in B, effectively moving them toward the bottom. - Elements with
threshold < index ≤ threshold + deltaare pushed without rotation, staying near the top of B. - All others are skipped with a rotation in A.
Heuristic Behavior:
This strategy encourages value stratification in stack B:
- Low-index (small) values accumulate in the center/bottom of B.
- High-index (large) values accumulate near the top and bottom, due to deferred pushing.
- This naturally forms a visual shape in B resembling the letter K, especially when stack B is represented as a vertical list.
Why K-shape?
Because of the dual-path:
- Low values are pushed early and rotated down, forming the lower leg of the K.
- Mid values are pushed later and remain at the top — center stroke.
- High values rotate in A until they become small enough to be pushed, then settle differently — upper leg.

K-Shape
Heuristic Rationale for Delta (Δ)
The Δ parameter plays a critical role in the K-Distribution Sort strategy. It controls the granularity of the progressive threshold used when pushing elements from stack A to stack B.
- Smaller Δ values yield a more ordered distribution in B, as smaller elements are pushed earlier and rotated deeper. This tends to produce a nearly sorted B (visually forming the diagonal stem of the “K”), resulting in a low-cost B→A reconstruction. However, this comes at the expense of a more expensive A→B phase due to the increased number of rotations and comparisons.
- Larger Δ values reduce the number of operations needed to push from A to B by accepting larger value ranges per threshold step. This speeds up the first phase, but the resulting B is more chaotic, which leads to a costlier and less predictable B→A reconstruction.

Delta
The empirical formula:
Δ = ⌊n / 20⌋ + 7
where n is the initial size of stack A, strikes a practical balance between both extremes. It ensures:
- A progressive structuring of B that visually forms the characteristic “K” shape.
- A well-distributed workload across both phases of the sort.
- Robustness across a wide range of input sizes without requiring tuning.
This heuristic has proven effective in achieving a good trade-off between operation count and sorting regularity in the push_swap constraint model.
Complexity and Applications:
- It is not optimal in terms of moves, but it greatly reduces chaos in B.
- The K-shape makes greedy or ordered reintegration (B→A) easier and faster.
- Best suited for
n ∈ [50, 500]elements inpush_swapprojects, though it remains effective and reliable for a broader range from around 10 to 1000 elements.
Algorithm — K-Distribution Sort
The following pseudocode presents the core logic of the K-Distribution Sort, which incrementally pushes values from stack A to stack B using a dynamic threshold and a tunable delta parameter. This strategy balances order in stack B against the cost of transitions.
void K_Distribution_Sort(Stack *a, Stack *b)
{
int n = a->size;
int delta = n / 20 + 7;
int threshold = 0;
while (!is_empty(a))
{
if (a->top->index <= threshold + delta)
{
push(a, b); // pb
if (b->top->index <= threshold)
rotate(b); // rb
threshold++;
}
else
{
rotate(a); // ra
}
}
}
Notes:
Stackis a structure with pointers to nodes, each of which holds a uniqueindexcorresponding to the final sorted order.push(a, b)removes the top element from stackaand pushes it onto stackb.rotate(x)performs a cyclic rotation: the top becomes the bottom.
This routine progressively builds stack B into a semi-ordered structure with lower-index elements concentrated near the bottom. The resulting distribution optimizes the subsequent reintegration phase.
Part III — Finalize the push-swap
Naive Reintegration: B → A (Greedy Max Selection)
void Reintegration_Sort(Stack *a, Stack *b)
{
while (!is_empty(b))
{
int max_index = find_max_index(b);
int pos = position_of_index(b, max_index);
if (pos <= b->size / 2)
{
while (b->top->index != max_index)
rotate(b); // rb
}
else
{
while (b->top->index != max_index)
reverse_rotate(b); // rrb
}
push(b, a); // pa
}
}
This method simply retrieves the largest element in stack B at each step and pushes it to stack A. To minimize operations:
- If the max element is in the top half of B, it uses
rbto bring it up. - If it’s in the bottom half, it uses
rrb. - Once the element is at the top, it is pushed with
pa.
The result is that stack A gradually builds up in descending order, which, given the constraints of push-swap, is the final desired configuration.
This strategy is naive but effective when stack B already has a semi-sorted structure — such as the one produced by the K-distribution phase.
[embed]K-Shape naive but efficient sort
Auxiliary Functions
Reindexing
void reindex(Node *nodes, int size)
{
for (int i = 0; i < size; i++)
{
int rank = 0;
for (int j = 0; j < size; j++)
{
if (nodes[j].value < nodes[i].value)
rank++;
}
nodes[i].index = rank;
}
}
Explanation:
Assigns an index to each node based on its rank in sorted order, without changing original values or using dynamic memory. This normalization simplifies sorting logic by providing compact, sequential indexes (0..n-1).
Finding the Maximum Index
int find_max_index(Stack *s)
{
int max = s->top->index;
Node *node = s->top;
while (node)
{
if (node->index > max)
max = node->index;
node = node->next;
}
return max;
}
Explanation:
Returns the highest index value currently present in the stack.
Finding the Position of a Given Index
int position_of_index(Stack *s, int target_index)
{
Node *node = s->top;
int position = 0;
while (node)
{
if (node->index == target_index)
return position;
node = node->next;
position++;
}
return -1;
}
Explanation:
Returns the position (distance from the top) of the node containing the specified index within the stack, or -1 if the index is not found.
Conclusion
The algorithm presented in this article offers a pragmatic and elegant approach to solving the push-swap problem. With just a handful of well-chosen operations and a compact implementation, it achieves remarkably efficient behavior — even without any additional optimizations.
By relying on reindexing, simple stack manipulation, and a clever heuristic distribution strategy (K-Distribution), this method strikes a balance between clarity, performance, and code brevity.
Its strength lies precisely in its minimalism: short, readable, and easy to implement — making it an ideal choice for both learning and competition.
Beyond its simplicity, this algorithm opens the door to further optimizations. First, the second phase (from stack B back to A) can be made more efficient by introducing cost-based decision logic, selecting the element to move based on its total rotation cost. Second, once the full command list is generated, a post-processing pass can simplify it: sequences like pa followed immediately by pb can cancel out, and simultaneous operations like ra + rb can be merged into a single rr, significantly reducing the total number of instructions without altering the result.
For more advanced programmers aiming for optimal performance, more aggressive strategies can be employed. Rather than relying solely on raw movement cost, one can introduce weighted heuristics — assigning bonuses or penalties based on patterns, expected impact, or positional context within the stacks. These weights can be derived from harmonic means, ratios, or even intentionally chaotic functions, which, though unpredictable, may help escape local minima and yield shorter solutions through better global arrangements.
Additionally, a deeper post-processing phase can detect and compress longer instruction patterns: for example, repeated sequences like ra ra ra rb rb rb can be merged into rr rr rr.
It is also possible to enhance the K-distribution strategy itself by dividing stack A into dynamic chunks, where each chunk is progressively smaller — allowing finer control and increased ordering as the process advances. These variable-size segments can further improve the structure of B and facilitate a more efficient reintegration phase.
In the end, the goal is not to find a solution quickly, but to produce the most concise and efficient list of operations possible, where controlled complexity often leads to elegance.
[embed]Optimized chunked K-Shape
메타데이터
- post_id
- ae2d96d68376
- slug
- k-distribution-sort-applied-to-the-push-swap-problem-ae2d96d68376
- url
- https://medium.com/@brakebein42/k-distribution-sort-applied-to-the-push-swap-problem-ae2d96d68376
- canonical_url
- https://medium.com/@brakebein42/k-distribution-sort-applied-to-the-push-swap-problem-ae2d96d68376
- author_url
- https://medium.com/@brakebein42
- status
- ok
- fetched_at
- 2026-06-23 03:48:11