Skip to content

Commit 0681048

Browse files
committed
Added Smoothsort algorithm with tests [Sorts]
1 parent b316dcf commit 0681048

File tree

2 files changed

+218
-0
lines changed

2 files changed

+218
-0
lines changed
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
package com.thealgorithms.sorts;
2+
3+
/**
4+
* Implements the SmoothSort algorithm created by Edsger W. Dijkstra.
5+
*
6+
* <p>SmoothSort is an adaptive comparison sorting algorithm. It is a variation of
7+
* heapsort that can efficiently sort arrays that are already substantially
8+
* sorted, approaching an O(n) time complexity in the best case. Its worst-case
9+
* time complexity is O(n log n).
10+
*
11+
* <p>The algorithm works by building a series of "Leonardo heaps", which are
12+
* heaps that obey a specific size constraint based on Leonardo numbers.
13+
* The list is first partitioned into this implicit sequence of heaps, and then
14+
* the largest element is repeatedly extracted and placed in its final sorted
15+
* position.
16+
*
17+
* @see <a href="https://en.wikipedia.org/wiki/Smoothsort">Wikipedia: Smoothsort</a>
18+
*/
19+
public final class SmoothSort {
20+
21+
/**
22+
* Private constructor to prevent instantiation of this utility class.
23+
*/
24+
private SmoothSort() {}
25+
26+
// Leonardo numbers: L(0)=1, L(1)=1, L(k+2)=L(k+1)+L(k)-1
27+
// We store pre-calculated Leonardo numbers up to a reasonable limit.
28+
private static final int[] LP = {
29+
1, 1, 3, 5, 9, 15, 25, 41, 67, 109, 177, 287, 465, 753, 1219, 1973, 3193, 5167, 8361, 13529, 21891, 35421, 57313, 92735, 150049, 242785, 392835, 635621, 1028457, 1664079, 2692537, 4356617, 7049155, 11405773, 18454929, 29860703, 48315633, 78176337, 126491971, 204668309, 331160281, 535828591, 866988873,
30+
};
31+
32+
/**
33+
* Sorts an array in ascending order using the SmoothSort algorithm.
34+
*
35+
* @param <T> the type of elements in the array, which must be comparable.
36+
* @param arr the array to be sorted.
37+
*/
38+
public static <T extends Comparable<T>> void sort(T[] arr) {
39+
if (arr == null || arr.length <= 1) {
40+
return;
41+
}
42+
int n = arr.length;
43+
44+
// Bitmasks representing the sizes of the Leonardo heaps.
45+
// q: rightmost heap, r: size of the heap.
46+
int q = 1;
47+
int r = 0;
48+
int p = 1; // Represents the sequence of heap sizes.
49+
50+
// Build the series of Leonardo heaps.
51+
while (q < n) {
52+
r = q;
53+
q = q + p + 1;
54+
p = r;
55+
}
56+
57+
// Main sorting loop: iteratively place the largest element at the end.
58+
while (q > 1) {
59+
q = q - 1;
60+
int rshift = p;
61+
p = q - rshift;
62+
63+
while (rshift > 1) {
64+
sift(arr, p, rshift);
65+
rshift = rshift / 2;
66+
sift(arr, p - rshift, q - rshift + p);
67+
}
68+
trinkle(arr, p, q);
69+
}
70+
trinkle(arr, 0, 1);
71+
}
72+
73+
/**
74+
* Helper function to restore the heap property by "sifting" the root down.
75+
* This is used when the root of a heap is smaller than its children.
76+
*/
77+
private static <T extends Comparable<T>> void sift(T[] arr, int p, int q) {
78+
int r = q - 1;
79+
while (r > p) {
80+
if (arr[p].compareTo(arr[r]) < 0) {
81+
swap(arr, p, r);
82+
}
83+
r--;
84+
}
85+
}
86+
87+
/**
88+
* Helper function to insert a new element into the heap structure.
89+
* It "trinkles" the element up to its correct position.
90+
*/
91+
private static <T extends Comparable<T>> void trinkle(T[] arr, int p, int q) {
92+
int r = q - 1;
93+
while (p < r) {
94+
int maxIndex = p;
95+
if (arr[maxIndex].compareTo(arr[r]) < 0) {
96+
maxIndex = r;
97+
}
98+
99+
int p1 = p;
100+
while (p1 < q) {
101+
int child1 = p1 + 1;
102+
int child2 = q - (p1 - p);
103+
104+
if (child1 < q && arr[maxIndex].compareTo(arr[child1]) < 0) {
105+
maxIndex = child1;
106+
}
107+
if (child2 < q && arr[maxIndex].compareTo(arr[child2]) < 0) {
108+
maxIndex = child2;
109+
}
110+
111+
int nextP1 = child2;
112+
if (child1 >= q) {
113+
break;
114+
}
115+
p1 = nextP1;
116+
}
117+
118+
if (maxIndex == p) {
119+
break;
120+
}
121+
122+
swap(arr, p, maxIndex);
123+
p = maxIndex;
124+
}
125+
sift(arr, 0, p);
126+
}
127+
128+
/**
129+
* Swaps two elements in an array.
130+
*/
131+
private static <T> void swap(T[] arr, int i, int j) {
132+
T temp = arr[i];
133+
arr[i] = arr[j];
134+
arr[j] = temp;
135+
}
136+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package com.thealgorithms.sorts;
2+
3+
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
4+
5+
import org.junit.jupiter.api.DisplayName;
6+
import org.junit.jupiter.api.Test;
7+
8+
/**
9+
* Unit tests for the SmoothSort class.
10+
*/
11+
final class SmoothSortTest {
12+
13+
@Test
14+
@DisplayName("Test with an empty array")
15+
void testSortEmptyArray() {
16+
Integer[] array = {};
17+
SmoothSort.sort(array);
18+
assertArrayEquals(new Integer[] {}, array);
19+
}
20+
21+
@Test
22+
@DisplayName("Test with a single-element array")
23+
void testSortSingleElementArray() {
24+
Integer[] array = {42};
25+
SmoothSort.sort(array);
26+
assertArrayEquals(new Integer[] {42}, array);
27+
}
28+
29+
@Test
30+
@DisplayName("Test with a simple unsorted array of integers")
31+
void testSortSimpleIntegerArray() {
32+
Integer[] array = {5, 8, 3, 1, 9, 4, 7, 2, 6};
33+
Integer[] expected = {1, 2, 3, 4, 5, 6, 7, 8, 9};
34+
SmoothSort.sort(array);
35+
assertArrayEquals(expected, array);
36+
}
37+
38+
@Test
39+
@DisplayName("Test with an already sorted array (best case)")
40+
void testSortAlreadySortedArray() {
41+
Integer[] array = {10, 20, 30, 40, 50};
42+
Integer[] expected = {10, 20, 30, 40, 50};
43+
SmoothSort.sort(array);
44+
assertArrayEquals(expected, array);
45+
}
46+
47+
@Test
48+
@DisplayName("Test with a reverse sorted array (worst case)")
49+
void testSortReverseSortedArray() {
50+
Integer[] array = {55, 44, 33, 22, 11};
51+
Integer[] expected = {11, 22, 33, 44, 55};
52+
SmoothSort.sort(array);
53+
assertArrayEquals(expected, array);
54+
}
55+
56+
@Test
57+
@DisplayName("Test with an array containing duplicate elements")
58+
void testSortArrayWithDuplicates() {
59+
Integer[] array = {7, 2, 9, 2, 5, 7, 9, 1, 5};
60+
Integer[] expected = {1, 2, 2, 5, 5, 7, 7, 9, 9};
61+
SmoothSort.sort(array);
62+
assertArrayEquals(expected, array);
63+
}
64+
65+
@Test
66+
@DisplayName("Test with an array of strings")
67+
void testSortStringArray() {
68+
String[] array = {"banana", "apple", "cherry", "date", "fig"};
69+
String[] expected = {"apple", "banana", "cherry", "date", "fig"};
70+
SmoothSort.sort(array);
71+
assertArrayEquals(expected, array);
72+
}
73+
74+
@Test
75+
@DisplayName("Test with an array containing negative numbers")
76+
void testSortArrayWithNegativeNumbers() {
77+
Integer[] array = {-5, 8, -3, 0, 9, -4, 7, 2, -6};
78+
Integer[] expected = {-6, -5, -4, -3, 0, 2, 7, 8, 9};
79+
SmoothSort.sort(array);
80+
assertArrayEquals(expected, array);
81+
}
82+
}

0 commit comments

Comments
 (0)