Skip to content

Commit a8ef941

Browse files
committed
feat: implement Smooth Sort algorithm with detailed JavaDoc and test class
1 parent ca4bebc commit a8ef941

2 files changed

Lines changed: 181 additions & 0 deletions

File tree

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
package com.thealgorithms.sorts;
2+
3+
/**
4+
* Smooth Sort is an in-place, comparison-based sorting algorithm proposed by Edsger W. Dijkstra (1981).
5+
*
6+
* <p>It can be viewed as a variant of heapsort that maintains a forest of heap-ordered Leonardo trees
7+
* (trees whose sizes are Leonardo numbers). The algorithm is adaptive: when the input is already
8+
* sorted or nearly sorted, the heap invariants are often satisfied and the expensive rebalancing
9+
* operations do little work, yielding near-linear behavior.
10+
*
11+
* <p>Time Complexity:
12+
* <ul>
13+
* <li>Best case: O(n) for already sorted input</li>
14+
* <li>Average case: O(n log n)</li>
15+
* <li>Worst case: O(n log n)</li>
16+
* </ul>
17+
*
18+
* <p>Space Complexity: O(1) auxiliary space (in-place).
19+
*
20+
* @see <a href="https://en.wikipedia.org/wiki/Smoothsort">Smoothsort</a>
21+
* @see <a href="https://en.wikipedia.org/wiki/Leonardo_number">Leonardo numbers</a>
22+
* @see SortAlgorithm
23+
*/
24+
public class SmoothSort implements SortAlgorithm {
25+
26+
/**
27+
* Leonardo numbers (L(0) = L(1) = 1, L(k+2) = L(k+1) + L(k) + 1) up to the largest value that
28+
* fits into a signed 32-bit integer.
29+
*/
30+
private static final int[] LEONARDO = {
31+
1, 1, 3, 5, 9, 15, 25, 41, 67, 109,
32+
177, 287, 465, 753, 1219, 1973, 3193, 5167, 8361, 13529,
33+
21891, 35421, 57313, 92735, 150049, 242785, 392835, 635621, 1028457, 1664079,
34+
2692537, 4356617, 7049155, 11405773, 18454929, 29860703, 48315633, 78176337, 126491971, 204668309,
35+
331160281, 535828591, 866988873, 1402817465
36+
};
37+
38+
/**
39+
* Sorts the given array in ascending order using Smooth Sort.
40+
*
41+
* @param array the array to sort
42+
* @param <T> the element type
43+
* @return the sorted array
44+
*/
45+
@Override
46+
public <T extends Comparable<T>> T[] sort(final T[] array) {
47+
if (array.length < 2) {
48+
return array;
49+
}
50+
51+
final int last = array.length - 1;
52+
53+
// The forest shape is encoded as (p, pshift): p is a bit-vector of present tree orders,
54+
// shifted right by pshift. pshift is the order of the rightmost (current) Leonardo tree.
55+
long p = 1L;
56+
int pshift = 1;
57+
58+
int head = 0;
59+
while (head < last) {
60+
if ((p & 3L) == 3L) {
61+
sift(array, pshift, head);
62+
p >>>= 2;
63+
pshift += 2;
64+
} else {
65+
// Add a new singleton tree; if it will not be merged anymore, we must fully trinkle.
66+
if (LEONARDO[pshift - 1] >= last - head) {
67+
trinkle(array, p, pshift, head, false);
68+
} else {
69+
// This tree will be merged later, so it is enough to restore its internal heap property.
70+
sift(array, pshift, head);
71+
}
72+
73+
if (pshift == 1) {
74+
// If L(1) is used, the new singleton is L(0).
75+
p <<= 1;
76+
pshift = 0;
77+
} else {
78+
// Otherwise, shift to order 1 and append a singleton of order 1.
79+
p <<= (pshift - 1);
80+
pshift = 1;
81+
}
82+
}
83+
84+
p |= 1L;
85+
head++;
86+
}
87+
88+
trinkle(array, p, pshift, head, false);
89+
90+
// Repeatedly remove the maximum (always at head) by shrinking the heap region.
91+
while (pshift != 1 || p != 1L) {
92+
if (pshift <= 1) {
93+
// Rightmost tree is a singleton (order 0 or 1). Move to the previous tree root.
94+
final long mask = p & ~1L;
95+
final int shift = Long.numberOfTrailingZeros(mask);
96+
p >>>= shift;
97+
pshift += shift;
98+
} else {
99+
// Split a tree of order (pshift) into two children trees of orders (pshift-1) and (pshift-2).
100+
p <<= 2;
101+
p ^= 7L;
102+
pshift -= 2;
103+
104+
trinkle(array, p >>> 1, pshift + 1, head - LEONARDO[pshift] - 1, true);
105+
trinkle(array, p, pshift, head - 1, true);
106+
}
107+
108+
head--;
109+
}
110+
111+
return array;
112+
}
113+
114+
private static <T extends Comparable<T>> void sift(final T[] array, int order, int root) {
115+
final T value = array[root];
116+
117+
while (order > 1) {
118+
final int right = root - 1;
119+
final int left = root - 1 - LEONARDO[order - 2];
120+
121+
if (!SortUtils.less(value, array[left]) && !SortUtils.less(value, array[right])) {
122+
break;
123+
}
124+
125+
if (!SortUtils.less(array[left], array[right])) {
126+
array[root] = array[left];
127+
root = left;
128+
order -= 1;
129+
} else {
130+
array[root] = array[right];
131+
root = right;
132+
order -= 2;
133+
}
134+
}
135+
136+
array[root] = value;
137+
}
138+
139+
private static <T extends Comparable<T>> void trinkle(final T[] array, long p, int order, int root, boolean trusty) {
140+
final T value = array[root];
141+
142+
while (p != 1L) {
143+
final int stepson = root - LEONARDO[order];
144+
145+
if (!SortUtils.less(value, array[stepson])) {
146+
break;
147+
}
148+
149+
if (!trusty && order > 1) {
150+
final int right = root - 1;
151+
final int left = root - 1 - LEONARDO[order - 2];
152+
153+
if (!SortUtils.less(array[right], array[stepson]) || !SortUtils.less(array[left], array[stepson])) {
154+
break;
155+
}
156+
}
157+
158+
array[root] = array[stepson];
159+
root = stepson;
160+
161+
final long mask = p & ~1L;
162+
final int shift = Long.numberOfTrailingZeros(mask);
163+
p >>>= shift;
164+
order += shift;
165+
trusty = false;
166+
}
167+
168+
if (!trusty) {
169+
array[root] = value;
170+
sift(array, order, root);
171+
}
172+
}
173+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.thealgorithms.sorts;
2+
3+
public class SmoothSortTest extends SortingAlgorithmTest {
4+
@Override
5+
SortAlgorithm getSortAlgorithm() {
6+
return new SmoothSort();
7+
}
8+
}

0 commit comments

Comments
 (0)