diff --git a/src/main/java/com/thealgorithms/datastructures/heaps/LeonardoHeap.java b/src/main/java/com/thealgorithms/datastructures/heaps/LeonardoHeap.java new file mode 100644 index 000000000000..7d43c2db209a --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/heaps/LeonardoHeap.java @@ -0,0 +1,203 @@ +package com.thealgorithms.datastructures.heaps; + +import com.thealgorithms.bitmanipulation.SingleBitOperations; +import com.thealgorithms.maths.LeonardoNumber; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Wikipedia: https://en.wikipedia.org/wiki/Smoothsort + */ +public class LeonardoHeap> { + + private int levelTracker = 0; + private final List heap = new ArrayList<>(); + + public LeonardoHeap() { + } + + private void decreaseLevelTracker() { + final int lastTreeLevel = getRightMostTree(); + levelTracker = SingleBitOperations.clearBit(levelTracker, lastTreeLevel); + if (lastTreeLevel != 0 && lastTreeLevel != 1) { + levelTracker = SingleBitOperations.setBit(levelTracker, lastTreeLevel - 1); + levelTracker = SingleBitOperations.setBit(levelTracker, lastTreeLevel - 2); + } + } + + private void increaseLevelTracker() { + final List consecutiveTreeIndices = findConsecutiveTreeIndices(levelTracker); + if (consecutiveTreeIndices.get(0) != -1) { + // if 0th or 1st index is -1 that implies there are no concequtive trees + levelTracker = SingleBitOperations.clearBit(levelTracker, consecutiveTreeIndices.get(0)); + levelTracker = SingleBitOperations.clearBit(levelTracker, consecutiveTreeIndices.get(1)); + levelTracker = SingleBitOperations.setBit(levelTracker, consecutiveTreeIndices.get(1) + 1); + } else if ((levelTracker & 2) == 0) { + levelTracker = SingleBitOperations.setBit(levelTracker, 1); + } else { + levelTracker = SingleBitOperations.setBit(levelTracker, 0); + } + } + + private void maxHeapifyTree(int rootNodeIndex, int currentLevel) { + // A leonardo tree of level n is just 1 node(the root) plus the leonardo tree of n-1 level(left child) plus leonardo tree of n-2 level(right child) + // To maxheapify a leonardo tree we need to compare the current root and roots of it's left and right subtree + // We recursively hepify the left and right subtrees using the currentLevel + + // BASE CASE + if (currentLevel == 0 || currentLevel == 1) { + return; // Trees with one node are in already max-heapified. + } + + final int currentRootNodeIndex = rootNodeIndex; + final int rightChildIndex = rootNodeIndex - 1; + final int leftChildIndex = rootNodeIndex - LeonardoNumber.leonardoNumber(currentLevel - 2) - 1; + final int childIndexForSwap = (heap.get(rightChildIndex).compareTo(heap.get(leftChildIndex)) >= 0) ? rightChildIndex : leftChildIndex; + + if (heap.get(childIndexForSwap).compareTo(heap.get(currentRootNodeIndex)) > 0) { + swap(currentRootNodeIndex, childIndexForSwap); + if (childIndexForSwap == rightChildIndex) { + maxHeapifyTree(rightChildIndex, currentLevel - 2); + } else { // swap happened with the left child + maxHeapifyTree(leftChildIndex, currentLevel - 1); + } + } + } + + private void shiftRootAndRestoreHeap() { + + if (heap.isEmpty()) { + return; + } + + final Integer[] currentTreeLevels = findAllTreeIndices(levelTracker); + ArrayList rootNodeIndices = getRootNodeIndices(currentTreeLevels); + + int rootNodeIndexForHeapify = rootNodeIndices.getLast(); + int treeLevelforHeapify = currentTreeLevels[currentTreeLevels.length - 1]; + + for (int i = 1; i < rootNodeIndices.size(); i++) { + + int currentRootNodeIndex = rootNodeIndices.get(i); + int prevRootNodeIndex = rootNodeIndices.get(i - 1); + int j = i; + while (compareRoots(currentRootNodeIndex, prevRootNodeIndex)) { + final int currentLevel = currentTreeLevels[j]; + boolean swapRequired = compareChildren(currentRootNodeIndex, prevRootNodeIndex, currentLevel); + if (swapRequired) { + // compare child and swap + swap(prevRootNodeIndex, currentRootNodeIndex); + rootNodeIndexForHeapify = prevRootNodeIndex; + treeLevelforHeapify = currentTreeLevels[j - 1]; + } else { + maxHeapifyTree(currentRootNodeIndex, currentLevel); + } + --j; + + if (j == 0) { + // We arrived at the left most tree. Do a maxheapifyTree if a swap had occurred + if (swapRequired) { + maxHeapifyTree(rootNodeIndexForHeapify, treeLevelforHeapify); + } + break; + } + currentRootNodeIndex = rootNodeIndices.get(j); + prevRootNodeIndex = rootNodeIndices.get(j - 1); + } + } + + maxHeapifyTree(rootNodeIndexForHeapify, treeLevelforHeapify); // In case of insert and no swap. + } + + private int getRightMostTree() { + // Isolate the rightmost set bit + int isolatedBit = levelTracker & -levelTracker; + int position = 0; + + while (isolatedBit > 1) { + isolatedBit >>= 1; + position++; + } + + return position; + } + + private void swap(int i, int j) { + Collections.swap(heap, i, j); + } + + public void addElement(T element) { + increaseLevelTracker(); + heap.add(element); + shiftRootAndRestoreHeap(); + } + + public T removeElement() { + decreaseLevelTracker(); + final T element = heap.removeLast(); + shiftRootAndRestoreHeap(); + + return element; + } + + private static List findConsecutiveTreeIndices(int num) { + int prevOneIndex = -1; + int currentLevel = 0; + + List answer = new ArrayList<>(); + answer.add(-1); + answer.add(-1); + + for (int i = 0; num > 0; i++) { + currentLevel = num & 1; + if (currentLevel == 1) { + if (prevOneIndex != -1) { + answer.set(0, prevOneIndex); + answer.set(1, i); + } + prevOneIndex = i; + } else { + prevOneIndex = -1; + } + num >>>= 1; + } + return answer; + } + + private static Integer[] findAllTreeIndices(int num) { + List setBitIndexes = new ArrayList<>(); + for (int i = Integer.SIZE - 1; i >= 0; i--) { + if ((num & (1 << i)) != 0) { + setBitIndexes.add(i); + } + } + return setBitIndexes.toArray(new Integer[0]); + } + + private boolean compareChildren(int currentRootNodeIndex, int prevRootNodeIndex, int currentLevel) { + if (currentLevel <= 1) { + // if there are no children to compare (L1 or L0 tree) return true + // because we already know that element at prevRootNodeIndex is greater than element at currentRootNodeIndex + // so a swap will be needed + return true; + } + final int rightChildIndex = currentRootNodeIndex - 1; + final int leftChildIndex = currentRootNodeIndex - 1 - LeonardoNumber.leonardoNumber(currentLevel - 2); + return heap.get(prevRootNodeIndex).compareTo(heap.get(rightChildIndex)) > 0 && heap.get(prevRootNodeIndex).compareTo(heap.get(leftChildIndex)) > 0; + } + + private boolean compareRoots(int currentRootNodeIndex, int prevRootNodeIndex) { + return heap.get(prevRootNodeIndex).compareTo(heap.get(currentRootNodeIndex)) > 0; + } + + private ArrayList getRootNodeIndices(Integer[] currentTreeLevels) { + int previousTreeSizeCumulative = 0; + ArrayList rootNodeIndices = new ArrayList<>(); + for (int i = 0; i < currentTreeLevels.length; i++) { + rootNodeIndices.add(previousTreeSizeCumulative + LeonardoNumber.leonardoNumber(currentTreeLevels[i]) - 1); + previousTreeSizeCumulative = previousTreeSizeCumulative + LeonardoNumber.leonardoNumber(currentTreeLevels[i]); + } + return rootNodeIndices; + } +} 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..244eb11ce23c --- /dev/null +++ b/src/main/java/com/thealgorithms/sorts/SmoothSort.java @@ -0,0 +1,30 @@ +package com.thealgorithms.sorts; + +import com.thealgorithms.datastructures.heaps.LeonardoHeap; + +/** + * Wikipedia: https://en.wikipedia.org/wiki/Smoothsort + */ +public final class SmoothSort implements SortAlgorithm { + + public SmoothSort() { + } + + private static > void smoothSort(T[] array) { + LeonardoHeap leonardoHeap = new LeonardoHeap(); + + for (final var element : array) { + leonardoHeap.addElement(element); + } + + for (int i = 0; i < array.length; i++) { + array[array.length - i - 1] = leonardoHeap.removeElement(); + } + } + + @Override + public > T[] sort(T[] array) { + smoothSort(array); + return array; + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/heaps/LeonardoHeapTest.java b/src/test/java/com/thealgorithms/datastructures/heaps/LeonardoHeapTest.java new file mode 100644 index 000000000000..922adfbfcb0a --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/heaps/LeonardoHeapTest.java @@ -0,0 +1,121 @@ +package com.thealgorithms.datastructures.heaps; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class LeonardoHeapTest { + + @Test + public void testAddElement() { + LeonardoHeap heap = new LeonardoHeap<>(); + heap.addElement(5); + heap.addElement(3); + heap.addElement(8); + + assertEquals(8, heap.removeElement()); // Max element should be 8 + assertEquals(5, heap.removeElement()); + assertEquals(3, heap.removeElement()); + } + + @Test + public void testRemoveElement() { + LeonardoHeap heap = new LeonardoHeap<>(); + heap.addElement(10); + heap.addElement(20); + heap.addElement(5); + + assertEquals(20, heap.removeElement()); + assertEquals(10, heap.removeElement()); + assertEquals(5, heap.removeElement()); + } + + @Test + public void testAddElementStrings() { + LeonardoHeap heap = new LeonardoHeap<>(); + heap.addElement("z"); + heap.addElement("a"); + heap.addElement("x"); + heap.addElement("b"); + heap.addElement("y"); + + assertEquals("z", heap.removeElement()); // Max element should be z + assertEquals("y", heap.removeElement()); + assertEquals("x", heap.removeElement()); + assertEquals("b", heap.removeElement()); + assertEquals("a", heap.removeElement()); + } + + @Test + public void testRemoveElementString() { + LeonardoHeap heap = new LeonardoHeap<>(); + heap.addElement("z"); + heap.addElement("a"); + heap.addElement("x"); + + assertEquals("z", heap.removeElement()); + assertEquals("x", heap.removeElement()); + assertEquals("a", heap.removeElement()); + } + + @Test + public void testAlwaysCurrentMaxElementIsRemoved() { + LeonardoHeap heap = new LeonardoHeap<>(); + heap.addElement(5); + heap.addElement(8); + heap.addElement(7); + heap.addElement(3); + + heap.addElement(4); + heap.addElement(4); + heap.addElement(4); + heap.addElement(6); + + heap.addElement(8); + heap.addElement(8); + + assertEquals(8, heap.removeElement()); + assertEquals(8, heap.removeElement()); + assertEquals(8, heap.removeElement()); + assertEquals(7, heap.removeElement()); + + assertEquals(6, heap.removeElement()); + assertEquals(5, heap.removeElement()); + assertEquals(4, heap.removeElement()); + assertEquals(4, heap.removeElement()); + + assertEquals(4, heap.removeElement()); + assertEquals(3, heap.removeElement()); + } + + @Test + public void testForCompareChildAndSwap() { + LeonardoHeap heap = new LeonardoHeap<>(); + Integer[] elements = {5, 33, 40, 28, 95, 29, 88, 94, 12, 84, 15, 33, 2, 52, 37, 62, 48, 13, 61, 59}; + + for (Integer element : elements) { + heap.addElement(element); + } + + assertEquals(95, heap.removeElement()); + assertEquals(94, heap.removeElement()); + assertEquals(88, heap.removeElement()); + assertEquals(84, heap.removeElement()); + assertEquals(62, heap.removeElement()); + assertEquals(61, heap.removeElement()); + assertEquals(59, heap.removeElement()); + assertEquals(52, heap.removeElement()); + assertEquals(48, heap.removeElement()); + assertEquals(40, heap.removeElement()); + assertEquals(37, heap.removeElement()); + assertEquals(33, heap.removeElement()); + assertEquals(33, heap.removeElement()); + assertEquals(29, heap.removeElement()); + assertEquals(28, heap.removeElement()); + assertEquals(15, heap.removeElement()); + assertEquals(13, heap.removeElement()); + assertEquals(12, heap.removeElement()); + assertEquals(5, heap.removeElement()); + assertEquals(2, heap.removeElement()); + } +} 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(); + } +}