From a8ef94149acceaaaf5143b234ef9c89c766fd30c Mon Sep 17 00:00:00 2001
From: Ahmed Allam <60698204+AllamF5J@users.noreply.github.com>
Date: Fri, 9 Jan 2026 02:48:38 +0200
Subject: [PATCH 1/6] feat: implement Smooth Sort algorithm with detailed
JavaDoc and test class
---
.../com/thealgorithms/sorts/SmoothSort.java | 173 ++++++++++++++++++
.../thealgorithms/sorts/SmoothSortTest.java | 8 +
2 files changed, 181 insertions(+)
create mode 100644 src/main/java/com/thealgorithms/sorts/SmoothSort.java
create mode 100644 src/test/java/com/thealgorithms/sorts/SmoothSortTest.java
diff --git a/src/main/java/com/thealgorithms/sorts/SmoothSort.java b/src/main/java/com/thealgorithms/sorts/SmoothSort.java
new file mode 100644
index 000000000000..7e881e84ee62
--- /dev/null
+++ b/src/main/java/com/thealgorithms/sorts/SmoothSort.java
@@ -0,0 +1,173 @@
+package com.thealgorithms.sorts;
+
+/**
+ * Smooth Sort is an in-place, comparison-based sorting algorithm proposed by Edsger W. Dijkstra (1981).
+ *
+ *
It can be viewed as a variant of heapsort that maintains a forest of heap-ordered Leonardo trees
+ * (trees whose sizes are Leonardo numbers). The algorithm is adaptive: when the input is already
+ * sorted or nearly sorted, the heap invariants are often satisfied and the expensive rebalancing
+ * operations do little work, yielding near-linear behavior.
+ *
+ *
Time Complexity:
+ *
+ * - Best case: O(n) for already sorted input
+ * - Average case: O(n log n)
+ * - Worst case: O(n log n)
+ *
+ *
+ * Space Complexity: O(1) auxiliary space (in-place).
+ *
+ * @see Smoothsort
+ * @see Leonardo numbers
+ * @see SortAlgorithm
+ */
+public class SmoothSort implements SortAlgorithm {
+
+ /**
+ * Leonardo numbers (L(0) = L(1) = 1, L(k+2) = L(k+1) + L(k) + 1) up to the largest value that
+ * fits into a signed 32-bit integer.
+ */
+ private static final int[] LEONARDO = {
+ 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, 1402817465
+ };
+
+ /**
+ * Sorts the given array in ascending order using Smooth Sort.
+ *
+ * @param array the array to sort
+ * @param the element type
+ * @return the sorted array
+ */
+ @Override
+ public > T[] sort(final T[] array) {
+ if (array.length < 2) {
+ return array;
+ }
+
+ final int last = array.length - 1;
+
+ // The forest shape is encoded as (p, pshift): p is a bit-vector of present tree orders,
+ // shifted right by pshift. pshift is the order of the rightmost (current) Leonardo tree.
+ long p = 1L;
+ int pshift = 1;
+
+ int head = 0;
+ while (head < last) {
+ if ((p & 3L) == 3L) {
+ sift(array, pshift, head);
+ p >>>= 2;
+ pshift += 2;
+ } else {
+ // Add a new singleton tree; if it will not be merged anymore, we must fully trinkle.
+ if (LEONARDO[pshift - 1] >= last - head) {
+ trinkle(array, p, pshift, head, false);
+ } else {
+ // This tree will be merged later, so it is enough to restore its internal heap property.
+ sift(array, pshift, head);
+ }
+
+ if (pshift == 1) {
+ // If L(1) is used, the new singleton is L(0).
+ p <<= 1;
+ pshift = 0;
+ } else {
+ // Otherwise, shift to order 1 and append a singleton of order 1.
+ p <<= (pshift - 1);
+ pshift = 1;
+ }
+ }
+
+ p |= 1L;
+ head++;
+ }
+
+ trinkle(array, p, pshift, head, false);
+
+ // Repeatedly remove the maximum (always at head) by shrinking the heap region.
+ while (pshift != 1 || p != 1L) {
+ if (pshift <= 1) {
+ // Rightmost tree is a singleton (order 0 or 1). Move to the previous tree root.
+ final long mask = p & ~1L;
+ final int shift = Long.numberOfTrailingZeros(mask);
+ p >>>= shift;
+ pshift += shift;
+ } else {
+ // Split a tree of order (pshift) into two children trees of orders (pshift-1) and (pshift-2).
+ p <<= 2;
+ p ^= 7L;
+ pshift -= 2;
+
+ trinkle(array, p >>> 1, pshift + 1, head - LEONARDO[pshift] - 1, true);
+ trinkle(array, p, pshift, head - 1, true);
+ }
+
+ head--;
+ }
+
+ return array;
+ }
+
+ private static > void sift(final T[] array, int order, int root) {
+ final T value = array[root];
+
+ while (order > 1) {
+ final int right = root - 1;
+ final int left = root - 1 - LEONARDO[order - 2];
+
+ if (!SortUtils.less(value, array[left]) && !SortUtils.less(value, array[right])) {
+ break;
+ }
+
+ if (!SortUtils.less(array[left], array[right])) {
+ array[root] = array[left];
+ root = left;
+ order -= 1;
+ } else {
+ array[root] = array[right];
+ root = right;
+ order -= 2;
+ }
+ }
+
+ array[root] = value;
+ }
+
+ private static > void trinkle(final T[] array, long p, int order, int root, boolean trusty) {
+ final T value = array[root];
+
+ while (p != 1L) {
+ final int stepson = root - LEONARDO[order];
+
+ if (!SortUtils.less(value, array[stepson])) {
+ break;
+ }
+
+ if (!trusty && order > 1) {
+ final int right = root - 1;
+ final int left = root - 1 - LEONARDO[order - 2];
+
+ if (!SortUtils.less(array[right], array[stepson]) || !SortUtils.less(array[left], array[stepson])) {
+ break;
+ }
+ }
+
+ array[root] = array[stepson];
+ root = stepson;
+
+ final long mask = p & ~1L;
+ final int shift = Long.numberOfTrailingZeros(mask);
+ p >>>= shift;
+ order += shift;
+ trusty = false;
+ }
+
+ if (!trusty) {
+ array[root] = value;
+ sift(array, order, root);
+ }
+ }
+}
diff --git a/src/test/java/com/thealgorithms/sorts/SmoothSortTest.java b/src/test/java/com/thealgorithms/sorts/SmoothSortTest.java
new file mode 100644
index 000000000000..8df0502e80e7
--- /dev/null
+++ b/src/test/java/com/thealgorithms/sorts/SmoothSortTest.java
@@ -0,0 +1,8 @@
+package com.thealgorithms.sorts;
+
+public class SmoothSortTest extends SortingAlgorithmTest {
+ @Override
+ SortAlgorithm getSortAlgorithm() {
+ return new SmoothSort();
+ }
+}
From 8bf1a0ea22f42dcb5ee88d004d7e1a27fceec26d Mon Sep 17 00:00:00 2001
From: Ahmed Allam <60698204+AllamF5J@users.noreply.github.com>
Date: Fri, 9 Jan 2026 03:46:48 +0200
Subject: [PATCH 2/6] style: format LEONARDO array for improved readability
with clang-format
---
src/main/java/com/thealgorithms/sorts/SmoothSort.java | 9 ++-------
1 file changed, 2 insertions(+), 7 deletions(-)
diff --git a/src/main/java/com/thealgorithms/sorts/SmoothSort.java b/src/main/java/com/thealgorithms/sorts/SmoothSort.java
index 7e881e84ee62..c45d6f1f02b2 100644
--- a/src/main/java/com/thealgorithms/sorts/SmoothSort.java
+++ b/src/main/java/com/thealgorithms/sorts/SmoothSort.java
@@ -27,13 +27,8 @@ public class SmoothSort implements SortAlgorithm {
* Leonardo numbers (L(0) = L(1) = 1, L(k+2) = L(k+1) + L(k) + 1) up to the largest value that
* fits into a signed 32-bit integer.
*/
- private static final int[] LEONARDO = {
- 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, 1402817465
- };
+ private static final int[] LEONARDO = {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, 1402817465};
/**
* Sorts the given array in ascending order using Smooth Sort.
From 2c340c82ad8afb5f85dd442b6cd228b838f74ba7 Mon Sep 17 00:00:00 2001
From: Ahmed Allam <60698204+AllamF5J@users.noreply.github.com>
Date: Fri, 9 Jan 2026 04:41:37 +0200
Subject: [PATCH 3/6] feat(sorts): add TournamentSort (winner-tree)
---
.../thealgorithms/sorts/TournamentSort.java | 84 +++++++++++++++++++
.../sorts/TournamentSortTest.java | 9 ++
2 files changed, 93 insertions(+)
create mode 100644 src/main/java/com/thealgorithms/sorts/TournamentSort.java
create mode 100644 src/test/java/com/thealgorithms/sorts/TournamentSortTest.java
diff --git a/src/main/java/com/thealgorithms/sorts/TournamentSort.java b/src/main/java/com/thealgorithms/sorts/TournamentSort.java
new file mode 100644
index 000000000000..ec51a1e2c0a9
--- /dev/null
+++ b/src/main/java/com/thealgorithms/sorts/TournamentSort.java
@@ -0,0 +1,84 @@
+package com.thealgorithms.sorts;
+
+import java.util.Arrays;
+
+/**
+ * Tournament Sort algorithm implementation.
+ *
+ * Tournament sort builds a winner tree (a complete binary tree storing the index
+ * of the smallest element in each subtree). It then repeatedly extracts the
+ * winner (minimum) and updates the path from the removed leaf to the root.
+ *
+ * Time Complexity:
+ * - Best case: O(n log n)
+ * - Average case: O(n log n)
+ * - Worst case: O(n log n)
+ *
+ * Space Complexity: O(n) – additional winner-tree storage
+ *
+ * @see Tournament Sort Algorithm
+ * @see SortAlgorithm
+ */
+public class TournamentSort implements SortAlgorithm {
+
+ @Override
+ public > T[] sort(T[] array) {
+ if (array == null || array.length < 2) {
+ return array;
+ }
+
+ final int n = array.length;
+ final int leafCount = nextPowerOfTwo(n);
+
+ // Winner tree represented as an array:
+ // - Leaves live at [leafCount .. 2*leafCount)
+ // - Internal nodes live at [1 .. leafCount)
+ // Each node stores an index into the original array or -1 for "empty".
+ final int[] tree = new int[2 * leafCount];
+ Arrays.fill(tree, -1);
+
+ for (int i = 0; i < n; i++) {
+ tree[leafCount + i] = i;
+ }
+
+ for (int node = leafCount - 1; node >= 1; node--) {
+ tree[node] = winnerIndex(array, tree[node * 2], tree[node * 2 + 1]);
+ }
+
+ final T[] result = array.clone();
+ for (int out = 0; out < n; out++) {
+ final int winner = tree[1];
+ result[out] = array[winner];
+
+ int node = leafCount + winner;
+ tree[node] = -1;
+
+ for (node /= 2; node >= 1; node /= 2) {
+ tree[node] = winnerIndex(array, tree[node * 2], tree[node * 2 + 1]);
+ }
+ }
+
+ System.arraycopy(result, 0, array, 0, n);
+ return array;
+ }
+
+ private static int nextPowerOfTwo(int n) {
+ int power = 1;
+ while (power < n) {
+ power <<= 1;
+ }
+ return power;
+ }
+
+ private static > int winnerIndex(T[] array, int leftIndex, int rightIndex) {
+ if (leftIndex == -1) {
+ return rightIndex;
+ }
+ if (rightIndex == -1) {
+ return leftIndex;
+ }
+
+ // If equal, prefer the left element to keep ordering deterministic.
+ return SortUtils.less(array[rightIndex], array[leftIndex]) ? rightIndex : leftIndex;
+ }
+}
diff --git a/src/test/java/com/thealgorithms/sorts/TournamentSortTest.java b/src/test/java/com/thealgorithms/sorts/TournamentSortTest.java
new file mode 100644
index 000000000000..cf3d6b5f04e2
--- /dev/null
+++ b/src/test/java/com/thealgorithms/sorts/TournamentSortTest.java
@@ -0,0 +1,9 @@
+package com.thealgorithms.sorts;
+
+public class TournamentSortTest extends SortingAlgorithmTest {
+
+ @Override
+ SortAlgorithm getSortAlgorithm() {
+ return new TournamentSort();
+ }
+}
From d658f1bae57924fddf0cacaea9d080901f26fccb Mon Sep 17 00:00:00 2001
From: Ahmed Allam <60698204+AllamF5J@users.noreply.github.com>
Date: Fri, 9 Jan 2026 05:13:20 +0200
Subject: [PATCH 4/6] test: add unit test for null array handling in
TournamentSort
---
.../com/thealgorithms/sorts/TournamentSortTest.java | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/src/test/java/com/thealgorithms/sorts/TournamentSortTest.java b/src/test/java/com/thealgorithms/sorts/TournamentSortTest.java
index cf3d6b5f04e2..91da746447a8 100644
--- a/src/test/java/com/thealgorithms/sorts/TournamentSortTest.java
+++ b/src/test/java/com/thealgorithms/sorts/TournamentSortTest.java
@@ -1,7 +1,17 @@
package com.thealgorithms.sorts;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+import org.junit.jupiter.api.Test;
+
public class TournamentSortTest extends SortingAlgorithmTest {
+ @Test
+ void shouldAcceptWhenNullArrayIsPassed() {
+ Integer[] array = null;
+ assertNull(getSortAlgorithm().sort(array));
+ }
+
@Override
SortAlgorithm getSortAlgorithm() {
return new TournamentSort();
From 591ef3e1e447be307c3511938583fc6d0c80026d Mon Sep 17 00:00:00 2001
From: Ahmed Allam <60698204+AllamF5J@users.noreply.github.com>
Date: Fri, 9 Jan 2026 05:46:15 +0200
Subject: [PATCH 5/6] feat(search): add rotated binary search
---
.../searches/RotatedBinarySearch.java | 60 +++++++++++++++++++
.../searches/RotatedBinarySearchTest.java | 46 ++++++++++++++
2 files changed, 106 insertions(+)
create mode 100644 src/main/java/com/thealgorithms/searches/RotatedBinarySearch.java
create mode 100644 src/test/java/com/thealgorithms/searches/RotatedBinarySearchTest.java
diff --git a/src/main/java/com/thealgorithms/searches/RotatedBinarySearch.java b/src/main/java/com/thealgorithms/searches/RotatedBinarySearch.java
new file mode 100644
index 000000000000..86099b2fa2fa
--- /dev/null
+++ b/src/main/java/com/thealgorithms/searches/RotatedBinarySearch.java
@@ -0,0 +1,60 @@
+package com.thealgorithms.searches;
+
+import com.thealgorithms.devutils.searches.SearchAlgorithm;
+
+/**
+ * Searches for a key in a sorted array that has been rotated at an unknown pivot.
+ *
+ *
+ * Example:
+ * {@code [8, 9, 10, 1, 2, 3, 4, 5, 6, 7]}
+ *
+ *
+ * This is a modified binary search. When the array contains no duplicates, the
+ * time complexity is {@code O(log n)}. With duplicates, the algorithm still
+ * works but may degrade to {@code O(n)} in the worst case.
+ *
+ * @see Search in rotated sorted array
+ * @see SearchAlgorithm
+ */
+public final class RotatedBinarySearch implements SearchAlgorithm {
+
+ @Override
+ public > int find(T[] array, T key) {
+ int left = 0;
+ int right = array.length - 1;
+
+ while (left <= right) {
+ int middle = (left + right) >>> 1;
+ int cmp = key.compareTo(array[middle]);
+ if (cmp == 0) {
+ return middle;
+ }
+
+ // Handle duplicates: if we cannot determine which side is sorted.
+ if (array[left].compareTo(array[middle]) == 0 && array[middle].compareTo(array[right]) == 0) {
+ left++;
+ right--;
+ continue;
+ }
+
+ // Left half is sorted.
+ if (array[left].compareTo(array[middle]) <= 0) {
+ if (array[left].compareTo(key) <= 0 && key.compareTo(array[middle]) < 0) {
+ right = middle - 1;
+ } else {
+ left = middle + 1;
+ }
+ } else {
+ // Right half is sorted.
+ if (array[middle].compareTo(key) < 0 && key.compareTo(array[right]) <= 0) {
+ left = middle + 1;
+ } else {
+ right = middle - 1;
+ }
+ }
+ }
+
+ return -1;
+ }
+}
diff --git a/src/test/java/com/thealgorithms/searches/RotatedBinarySearchTest.java b/src/test/java/com/thealgorithms/searches/RotatedBinarySearchTest.java
new file mode 100644
index 000000000000..43ba39e6380f
--- /dev/null
+++ b/src/test/java/com/thealgorithms/searches/RotatedBinarySearchTest.java
@@ -0,0 +1,46 @@
+package com.thealgorithms.searches;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+
+class RotatedBinarySearchTest {
+
+ @Test
+ void shouldFindElementInRotatedArrayLeftSide() {
+ RotatedBinarySearch search = new RotatedBinarySearch();
+ Integer[] array = {8, 9, 10, 11, 12, 1, 2, 3, 4, 5, 6, 7};
+ assertEquals(2, search.find(array, 10));
+ }
+
+ @Test
+ void shouldFindElementInRotatedArrayRightSide() {
+ RotatedBinarySearch search = new RotatedBinarySearch();
+ Integer[] array = {8, 9, 10, 11, 12, 1, 2, 3, 4, 5, 6, 7};
+ assertEquals(6, search.find(array, 2));
+ }
+
+ @Test
+ void shouldFindElementInNotRotatedArray() {
+ RotatedBinarySearch search = new RotatedBinarySearch();
+ Integer[] array = {1, 2, 3, 4, 5, 6, 7};
+ assertEquals(4, search.find(array, 5));
+ }
+
+ @Test
+ void shouldReturnMinusOneWhenNotFound() {
+ RotatedBinarySearch search = new RotatedBinarySearch();
+ Integer[] array = {4, 5, 6, 7, 0, 1, 2};
+ assertEquals(-1, search.find(array, 3));
+ }
+
+ @Test
+ void shouldHandleDuplicates() {
+ RotatedBinarySearch search = new RotatedBinarySearch();
+ Integer[] array = {2, 2, 2, 3, 4, 2};
+ int index = search.find(array, 3);
+ assertTrue(index >= 0 && index < array.length);
+ assertEquals(3, array[index]);
+ }
+}
From f02864126811b2ae6687b8c6d0df38b003426e30 Mon Sep 17 00:00:00 2001
From: Ahmed Allam <60698204+AllamF5J@users.noreply.github.com>
Date: Fri, 9 Jan 2026 06:13:30 +0200
Subject: [PATCH 6/6] test: add unit test for handling middle element in right
sorted half of rotated array
---
.../thealgorithms/searches/RotatedBinarySearchTest.java | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/src/test/java/com/thealgorithms/searches/RotatedBinarySearchTest.java b/src/test/java/com/thealgorithms/searches/RotatedBinarySearchTest.java
index 43ba39e6380f..1e6ab4c37fcc 100644
--- a/src/test/java/com/thealgorithms/searches/RotatedBinarySearchTest.java
+++ b/src/test/java/com/thealgorithms/searches/RotatedBinarySearchTest.java
@@ -35,6 +35,13 @@ void shouldReturnMinusOneWhenNotFound() {
assertEquals(-1, search.find(array, 3));
}
+ @Test
+ void shouldHandleWhenMiddleIsGreaterThanKeyInRightSortedHalf() {
+ RotatedBinarySearch search = new RotatedBinarySearch();
+ Integer[] array = {6, 7, 0, 1, 2, 3, 4, 5};
+ assertEquals(2, search.find(array, 0));
+ }
+
@Test
void shouldHandleDuplicates() {
RotatedBinarySearch search = new RotatedBinarySearch();