Skip to content

Implement Smooth Sort #5236

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from 39 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
9e5cb32
Adding Skeleton
JAPNITSINGH May 28, 2024
9ff4e85
Understanding Heap Sort
JAPNITSINGH May 29, 2024
69b24a8
Implement merge sort
JAPNITSINGH May 30, 2024
6de4059
Smooth Sort impl, needs cleaning.
JAPNITSINGH Jun 14, 2024
d6a14b6
Added test cases, improved code coverage.
JAPNITSINGH Jun 17, 2024
384f814
Refactorization of code
JAPNITSINGH Jun 18, 2024
29252e1
Merge branch 'TheAlgorithms:master' into Branch_Smooth_Sort
JAPNITSINGH Jun 18, 2024
4f0fd70
Fixed brackets and ran mvn checkstyle:checkstyle to ensure no more ch…
JAPNITSINGH Jun 18, 2024
0cdcba2
Fixed a tab space issue
JAPNITSINGH Jun 18, 2024
d32e435
Removed useless parenthesis.
JAPNITSINGH Jun 18, 2024
712868e
Re work, fix bugs, names etc.
JAPNITSINGH Jun 24, 2024
368db8a
Merge branch 'TheAlgorithms:master' into Branch_Smooth_Sort
JAPNITSINGH Jun 24, 2024
5f9d2e7
Merge branch 'Branch_Smooth_Sort' of github.com:JAPNITSINGH/Java into…
JAPNITSINGH Jun 24, 2024
990b183
Forgot to add test file
JAPNITSINGH Jun 24, 2024
0eda98e
Improved code coverage yo 99.9%
JAPNITSINGH Jun 24, 2024
aed3358
Removed the break statement to improve code coverage.
JAPNITSINGH Jun 24, 2024
ab905bf
Removed comments and used SortUtils
JAPNITSINGH Jun 25, 2024
65ca159
Merge branch 'TheAlgorithms:master' into Branch_Smooth_Sort
JAPNITSINGH Jun 25, 2024
d5e3b95
Merge branch 'Branch_Smooth_Sort' of github.com:JAPNITSINGH/Java into…
JAPNITSINGH Jun 25, 2024
ec02a6c
Use SingleBitOperations class.
JAPNITSINGH Jun 26, 2024
b19bd38
Conditional changes on when to call max_heapify.
JAPNITSINGH Jun 28, 2024
5d21701
Skeleton of LeonardoHeap.java
JAPNITSINGH Jun 29, 2024
951653f
Leonardo Heap implementeaton , TODO: Test cases
JAPNITSINGH Jul 4, 2024
79a5d20
Slight modifications to code
JAPNITSINGH Jul 5, 2024
9fe137c
Merge branch 'TheAlgorithms:master' into Branch_Smooth_Sort
JAPNITSINGH Jul 5, 2024
7aa1421
Merge branch 'Branch_Smooth_Sort' of github.com:JAPNITSINGH/Java into…
JAPNITSINGH Jul 5, 2024
645b29c
variable name modifications.
JAPNITSINGH Jul 8, 2024
89d9f51
Merge branch 'TheAlgorithms:master' into Branch_Smooth_Sort
JAPNITSINGH Jul 8, 2024
1adbd15
Added LeonardoHeapTest.java
JAPNITSINGH Jul 8, 2024
c249579
Merge branch 'Branch_Smooth_Sort' of github.com:JAPNITSINGH/Java into…
JAPNITSINGH Jul 8, 2024
edbf08a
UMTP_UNBOUND_METHOD_TEMPLATE_PARAMETER mvn spotbug fix
JAPNITSINGH Jul 8, 2024
cf1ce78
Merge branch 'master' into Branch_Smooth_Sort
JAPNITSINGH Jul 12, 2024
369cbeb
Merge branch 'master' into Branch_Smooth_Sort
JAPNITSINGH Jul 16, 2024
01ba460
Rename members and add tests
JAPNITSINGH Jul 18, 2024
fa0797f
pmd errors fix
JAPNITSINGH Jul 18, 2024
ba730d0
spotbug fix
JAPNITSINGH Jul 18, 2024
a8a5835
Added LeonardoHeapHelper.java
JAPNITSINGH Jul 19, 2024
5093f86
Merge branch 'TheAlgorithms:master' into Branch_Smooth_Sort
JAPNITSINGH Jul 19, 2024
761b848
Merge branch 'Branch_Smooth_Sort' of github.com:JAPNITSINGH/Java into…
JAPNITSINGH Jul 19, 2024
37c794b
Removed Helper Class, formatted to make items final.
JAPNITSINGH Jul 22, 2024
c0b8737
Rework on formatting.
JAPNITSINGH Jul 22, 2024
ad4c540
Reworking LeonardoHeap: shiftRootAndRestoreHeap
JAPNITSINGH Jul 24, 2024
0edabcd
Lint issues in LeonardoHeap
JAPNITSINGH Jul 24, 2024
6b4c415
style: use predecrement operator
vil02 Aug 6, 2024
174de3f
tests: add more checks into tests
vil02 Aug 6, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
package com.thealgorithms.datastructures.heaps;

import com.thealgorithms.bitmanipulation.SingleBitOperations;
import com.thealgorithms.maths.LeonardoNumber;
import java.util.ArrayList;
import java.util.List;

/**
* Wikipedia: https://en.wikipedia.org/wiki/Smoothsort
*/
public class LeonardoHeap<T extends Comparable<T>> {

private int levelTracker = 0;
private final List<T> heap = new ArrayList<T>();

public LeonardoHeap() {
}

private void decreaseLevelTracker() {
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() {
ArrayList<Integer> consecutiveTreeIndices = LeonardoHeapHelper.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.
}

int currentRootNodeIndex = rootNodeIndex;
int rightChildIndex = rootNodeIndex - 1;
int leftChildIndex = rootNodeIndex - LeonardoNumber.leonardoNumber(currentLevel - 2) - 1;
int childIndexForSwap = -1;

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
int currentRootNodeIndex = rootNodeIndex;
int rightChildIndex = rootNodeIndex - 1;
int leftChildIndex = rootNodeIndex - LeonardoNumber.leonardoNumber(currentLevel - 2) - 1;
int childIndexForSwap = -1;
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;
final int currentRootNodeIndex = rootNodeIndex;

Yes, the formatting in this repo is ugly.

if (heap.get(rightChildIndex).compareTo(heap.get(leftChildIndex)) >= 0) {
childIndexForSwap = rightChildIndex;
} else {
childIndexForSwap = 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() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you see some change to chop this method into few smaller ones?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be possible, I'll take a look into using some form of recursion instead of the while loop I am using. Recursion might give a bit more readability.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest to refactor this method into a shorter one.


if (heap.isEmpty()) {
return;
}

Integer[] currentTreeLevels = LeonardoHeapHelper.findAllTreeIndices(levelTracker);
int previousTreeSizeCumulative = 0;
ArrayList<Integer> rootNodeIndices = new ArrayList<Integer>();

// The number of roots are going to be same the the number of levels
// iterate over the currentTreeLevels and get roots

for (int i = 0; i < currentTreeLevels.length; i++) {
rootNodeIndices.add(previousTreeSizeCumulative + LeonardoNumber.leonardoNumber(currentTreeLevels[i]) - 1);
previousTreeSizeCumulative = previousTreeSizeCumulative + LeonardoNumber.leonardoNumber(currentTreeLevels[i]);
}

int rootNodeIndexForHeapify = rootNodeIndices.getLast();
int treeLevelforHeapify = currentTreeLevels[currentTreeLevels.length - 1];
boolean swaped = false;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
boolean swaped = false;
boolean swapped = false;

You can consider moving this declaration into the loop (smaller scope is always better). This should work, because this variable is always false at the end of of each loop.


for (int i = 1; i < rootNodeIndices.size(); i++) {

int currentRootNodeIndex = rootNodeIndices.get(i);
int prevRootNodeIndex = rootNodeIndices.get(i - 1);
int j = i;
while (heap.get(prevRootNodeIndex).compareTo(heap.get(currentRootNodeIndex)) > 0) {
int currentLevel = currentTreeLevels[j];
if (currentLevel > 1) {
// compare child and swap

int indexOfRightChild = rootNodeIndices.get(j) - 1; // right child is of level n-2
int indexOfLeftChild = rootNodeIndices.get(j) - 1 - LeonardoNumber.leonardoNumber(currentLevel - 2);
if (heap.get(prevRootNodeIndex).compareTo(heap.get(indexOfRightChild)) > 0 && heap.get(prevRootNodeIndex).compareTo(heap.get(indexOfLeftChild)) > 0) {
swap(prevRootNodeIndex, currentRootNodeIndex);
rootNodeIndexForHeapify = prevRootNodeIndex;
treeLevelforHeapify = currentTreeLevels[j - 1];
swaped = true;
} else {
maxHeapifyTree(currentRootNodeIndex, currentLevel);
swaped = false;
}
} else {
swap(prevRootNodeIndex, currentRootNodeIndex);
rootNodeIndexForHeapify = prevRootNodeIndex;
treeLevelforHeapify = currentTreeLevels[j - 1];
swaped = true;
}
j = j - 1;
if (j > 0) {
currentRootNodeIndex = rootNodeIndices.get(j);
prevRootNodeIndex = rootNodeIndices.get(j - 1);
} else {
// j = 0 reached the left most tree
break;
}
}

if (swaped) {
maxHeapifyTree(rootNodeIndexForHeapify, treeLevelforHeapify);
swaped = false;
}
}

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) {
T temp = heap.get(i);
heap.set(i, heap.get(j));
heap.set(j, temp);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
private void swap(int i, int j) {
T temp = heap.get(i);
heap.set(i, heap.get(j));
heap.set(j, temp);
}
private void swap(final int i, final int j) {
Collections.swap(heap, i, j);
}

make sure to

import java.util.Collections;


public void addElement(T element) {
increaseLevelTracker();
heap.add(element);
shiftRootAndRestoreHeap();
}

public T removeElement() {
decreaseLevelTracker();
T element = heap.removeLast();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
T element = heap.removeLast();
final T element = heap.removeLast();

shiftRootAndRestoreHeap();

return element;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.thealgorithms.datastructures.heaps;

import java.util.ArrayList;
import java.util.List;

// TODO: CHeck file name. Check if the file needs to be here or in bitwose operations.
public final class LeonardoHeapHelper {

private LeonardoHeapHelper() {
}

public static ArrayList<Integer> findConsecutiveTreeIndices(int num) {
int prevOneIndex = -1;
int currentLevel = 0;

ArrayList<Integer> answer = new ArrayList<Integer>();
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;
}

public static Integer[] findAllTreeIndices(int num) {
List<Integer> 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]);
}
}
30 changes: 30 additions & 0 deletions src/main/java/com/thealgorithms/sorts/SmoothSort.java
Original file line number Diff line number Diff line change
@@ -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 <T extends Comparable<T>> void smoothSort(T[] array) {
LeonardoHeap<T> leonardoHeap = new LeonardoHeap<T>();

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 extends Comparable<T>> T[] sort(T[] array) {
smoothSort(array);
return array;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package com.thealgorithms.datastructures.heaps;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.util.ArrayList;
import java.util.Arrays;
import org.junit.jupiter.api.Test;

// TODO: CHeck file name. Check if the file needs to be here or in bitwose operations.
public class LeonardoHeapHelperTest {

@Test
public void testConsecutiveTreeIndicesForZero() {
ArrayList<Integer> arrayList = LeonardoHeapHelper.findConsecutiveTreeIndices(0);
ArrayList<Integer> expectedList = new ArrayList<>(Arrays.asList(-1, -1));

assertEquals(expectedList, arrayList);
}

@Test
public void testConsecutiveTreeIndicesForEleven() {
ArrayList<Integer> arrayList = LeonardoHeapHelper.findConsecutiveTreeIndices(11);
ArrayList<Integer> expectedList = new ArrayList<>(Arrays.asList(0, 1));

assertEquals(expectedList, arrayList);
}

@Test
public void testConsecutiveTreeIndicesForSixteen() {
ArrayList<Integer> arrayList = LeonardoHeapHelper.findConsecutiveTreeIndices(16);
ArrayList<Integer> expectedList = new ArrayList<>(Arrays.asList(-1, -1));

assertEquals(expectedList, arrayList);
}

@Test
public void testConsecutiveTreeIndicesForTwentyFour() {
ArrayList<Integer> arrayList = LeonardoHeapHelper.findConsecutiveTreeIndices(24);
ArrayList<Integer> expectedList = new ArrayList<>(Arrays.asList(3, 4));

assertEquals(expectedList, arrayList);
}

@Test
public void testfindAllTreeIndicesForZero() {
Integer[] array = LeonardoHeapHelper.findAllTreeIndices(0);
assertEquals(0, array.length);
}

@Test
public void testfindAllTreeIndicesForEleven() {
Integer[] array = LeonardoHeapHelper.findAllTreeIndices(11);
Integer[] expectedArray = new Integer[] {3, 1, 0};

assertTrue(Arrays.equals(expectedArray, array));
}

@Test
public void testfindAllTreeIndicesForSixteen() {
Integer[] array = LeonardoHeapHelper.findAllTreeIndices(16);
Integer[] expectedArray = new Integer[] {4};

assertTrue(Arrays.equals(expectedArray, array));
}

@Test
public void testfindAllTreeIndicesForTwentyFour() {
Integer[] array = LeonardoHeapHelper.findAllTreeIndices(24);
Integer[] expectedArray = new Integer[] {4, 3};

assertTrue(Arrays.equals(expectedArray, array));
}
}
Loading