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 33 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,225 @@
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 leonardoLevelTracker;
private int leonardoHeapSize;
private final List<T> leonardoHeap;

public LeonardoHeap() {
this.leonardoHeap = new ArrayList<T>();
this.leonardoLevelTracker = 0;
this.leonardoHeapSize = 0;
}

public int getHeapsize() {
return this.leonardoHeapSize;
}

private void decreaseLeonardoLevelTracker() {
int lastTreeLevel = getRightMostTree();
leonardoLevelTracker = SingleBitOperations.clearBit(leonardoLevelTracker, lastTreeLevel);
if (lastTreeLevel != 0 && lastTreeLevel != 1) {
leonardoLevelTracker = SingleBitOperations.setBit(leonardoLevelTracker, lastTreeLevel - 1);
leonardoLevelTracker = SingleBitOperations.setBit(leonardoLevelTracker, lastTreeLevel - 2);
}
}

private void increaseLeonardoLevelTracker() {
ArrayList<Integer> consecutiveTreeIndices = findConsecutiveLeonardoTreeIndices(leonardoLevelTracker);
if (consecutiveTreeIndices.get(0) != -1) {
// if 0th or 1st index is -1 that implies there are no concequtive trees
leonardoLevelTracker = SingleBitOperations.clearBit(leonardoLevelTracker, consecutiveTreeIndices.get(0));
leonardoLevelTracker = SingleBitOperations.clearBit(leonardoLevelTracker, consecutiveTreeIndices.get(1));
leonardoLevelTracker = SingleBitOperations.setBit(leonardoLevelTracker, consecutiveTreeIndices.get(1) + 1);
} else if ((leonardoLevelTracker & 2) == 0) {
leonardoLevelTracker = SingleBitOperations.setBit(leonardoLevelTracker, 1);
} else {
leonardoLevelTracker = SingleBitOperations.setBit(leonardoLevelTracker, 0);
}
}

private void decreaseHeapSize() {
this.leonardoHeapSize--;
}

private void increaseHeapSize() {
this.leonardoHeapSize++;
}

private void maxHeapifyLeonardoTree(int rootNodeIndex, int currentLeonardoLevel) {
// 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 currentLeonardoLevel

// BASE CASE
if (currentLeonardoLevel == 0 || currentLeonardoLevel == 1) {
return; // Trees with one node are in already max-heapified.
}

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

if (leonardoHeap.get(rightChildIndex).compareTo(leonardoHeap.get(leftChildIndex)) >= 0) {
childIndexForSwap = rightChildIndex;
} else {
childIndexForSwap = leftChildIndex;
}

if (leonardoHeap.get(childIndexForSwap).compareTo(leonardoHeap.get(currentRootNodeIndex)) > 0) {
swap(currentRootNodeIndex, childIndexForSwap);
if (childIndexForSwap == rightChildIndex) {
maxHeapifyLeonardoTree(rightChildIndex, currentLeonardoLevel - 2);
} else { // swap happened with the left child
maxHeapifyLeonardoTree(leftChildIndex, currentLeonardoLevel - 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 (getHeapsize() == 0) {
return;
}

Integer[] currentLeonardoTreeLevels = findAllLeonardoTreeIndices();
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 currentLeonardoTreeLevels and get roots

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

int rootNodeIndexForHeapify = rootNodeIndices.getLast();
int leonardoTreeLevelforHeapify = currentLeonardoTreeLevels[currentLeonardoTreeLevels.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 (leonardoHeap.get(prevRootNodeIndex).compareTo(leonardoHeap.get(currentRootNodeIndex)) > 0) {
int currentLeonardoLevel = currentLeonardoTreeLevels[j];
if (currentLeonardoLevel > 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(currentLeonardoLevel - 2);
if (leonardoHeap.get(prevRootNodeIndex).compareTo(leonardoHeap.get(indexOfRightChild)) > 0 && leonardoHeap.get(prevRootNodeIndex).compareTo(leonardoHeap.get(indexOfLeftChild)) > 0) {
swap(prevRootNodeIndex, currentRootNodeIndex);
rootNodeIndexForHeapify = prevRootNodeIndex;
leonardoTreeLevelforHeapify = currentLeonardoTreeLevels[j - 1];
swaped = true;
} else {
maxHeapifyLeonardoTree(currentRootNodeIndex, currentLeonardoLevel);
swaped = false;
}
} else {
swap(prevRootNodeIndex, currentRootNodeIndex);
rootNodeIndexForHeapify = prevRootNodeIndex;
leonardoTreeLevelforHeapify = currentLeonardoTreeLevels[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) {
maxHeapifyLeonardoTree(rootNodeIndexForHeapify, leonardoTreeLevelforHeapify);
swaped = false;
}
}

maxHeapifyLeonardoTree(rootNodeIndexForHeapify, leonardoTreeLevelforHeapify); // In case of insert and no swap.
}

private int getRightMostTree() {
// Isolate the rightmost set bit
int isolatedBit = leonardoLevelTracker & -leonardoLevelTracker;
int position = 0;

while (isolatedBit > 1) {
isolatedBit >>= 1;
position++;
}

return position;
}

private static ArrayList<Integer> findConsecutiveLeonardoTreeIndices(int num) {
int prevOneIndex = -1;
int currentLevel;

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;
}
Copy link
Member

Choose a reason for hiding this comment

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

This should be move to a separate class.

Copy link
Author

@JAPNITSINGH JAPNITSINGH Jul 18, 2024

Choose a reason for hiding this comment

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

I can create a new class for this, probably with the name of LeonardoTreeHelper.java and add it's respective test file.

The other thing that can be done is that we can move this function to one of the bit manipulation classes? Since all this function is doing is returning first pair of indixes of consecutive 1 bits in an integer.

Which among these seems the right option @vil02?

Copy link
Member

Choose a reason for hiding this comment

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

@JAPNITSINGH sorry, this was my mistake. I somehow thought that this computes the Leonardo lumbers. Please move these two methods back to the LeonardoHeap. Sorry for the confusion.


private void swap(int i, int j) {
T temp = leonardoHeap.get(i);
leonardoHeap.set(i, leonardoHeap.get(j));
leonardoHeap.set(j, temp);
}

private Integer[] findAllLeonardoTreeIndices() {
List<Integer> setBitIndexes = new ArrayList<>();
for (int i = Integer.SIZE - 1; i >= 0; i--) {
if ((leonardoLevelTracker & (1 << i)) != 0) {
setBitIndexes.add(i);
}
}
return setBitIndexes.toArray(new Integer[0]);
}

public void addElement(T element) {
increaseLeonardoLevelTracker();
leonardoHeap.add(element);
increaseHeapSize();
shiftRootAndRestoreHeap();
}

public T removeElement() {
decreaseLeonardoLevelTracker();
decreaseHeapSize();
T element = leonardoHeap.removeLast();
shiftRootAndRestoreHeap();

return element;
}
}
31 changes: 31 additions & 0 deletions src/main/java/com/thealgorithms/sorts/SmoothSort.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
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 (int i = 0; i < array.length; i++) {
leonardoHeap.addElement(array[i]);
}

for (int i = 0; i < array.length; i++) {
final T maxElement = leonardoHeap.removeElement();
array[array.length - i - 1] = maxElement;
}
}

@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,98 @@
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 testEmpty() {
LeonardoHeap<Integer> heap = new LeonardoHeap<Integer>();
assertEquals(0, heap.getHeapsize());
}

@Test
public void testAddElement() {
LeonardoHeap<Integer> heap = new LeonardoHeap<>();
heap.addElement(5);
heap.addElement(3);
heap.addElement(8);

assertEquals(3, heap.getHeapsize());
assertEquals(8, heap.removeElement()); // Max element should be 8
}

@Test
public void testRemoveElement() {
LeonardoHeap<Integer> 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 testHeapSize() {
LeonardoHeap<Integer> heap = new LeonardoHeap<>();
assertEquals(0, heap.getHeapsize());

heap.addElement(1);
assertEquals(1, heap.getHeapsize());

heap.addElement(2);
assertEquals(2, heap.getHeapsize());

heap.removeElement();
assertEquals(1, heap.getHeapsize());

heap.removeElement();
assertEquals(0, heap.getHeapsize());
}

@Test
public void testAddElementStrings() {
LeonardoHeap<String> heap = new LeonardoHeap<String>();
heap.addElement("z");
heap.addElement("a");
heap.addElement("x");
heap.addElement("b");
heap.addElement("y");

assertEquals(5, heap.getHeapsize());
assertEquals("z", heap.removeElement()); // Max element should be 8
}

@Test
public void testRemoveElementString() {
LeonardoHeap<String> heap = new LeonardoHeap<String>();
heap.addElement("z");
heap.addElement("a");
heap.addElement("x");

assertEquals("z", heap.removeElement());
assertEquals("x", heap.removeElement());
assertEquals("a", heap.removeElement());
}

@Test
public void testHeapSizeString() {
LeonardoHeap<String> heap = new LeonardoHeap<String>();
assertEquals(0, heap.getHeapsize());

heap.addElement("z");
assertEquals(1, heap.getHeapsize());

heap.addElement("a");
assertEquals(2, heap.getHeapsize());

heap.removeElement();
assertEquals(1, heap.getHeapsize());

heap.removeElement();
assertEquals(0, heap.getHeapsize());
}
}
8 changes: 8 additions & 0 deletions src/test/java/com/thealgorithms/sorts/SmoothSortTest.java
Copy link
Member

Choose a reason for hiding this comment

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

If the answer to the above questions is yes, then if should be enough to derive the SmoothSortTest from SortingAlgorithmTest.

Copy link
Author

Choose a reason for hiding this comment

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

I believe it is possible, I just have to use generic T[] in place of Integer[] modify the code a little bit and use .compareTo() and it should be good. I shall work on it and update this PR. I'll comment here once changes are ready.

Copy link
Author

@JAPNITSINGH JAPNITSINGH Jun 22, 2024

Choose a reason for hiding this comment

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

I have updated the code, haven't pushed it yet. The smooth sort algorithm I implemented seems to be failing for most arrays of size greater than 700. I guess the problem is with how I am calculating the left child of a node. I am having a look into it. I shall provide an update here once I am done with the fix

Copy link
Author

Choose a reason for hiding this comment

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

I have updated to code, derived the class from SortAlgorithm, removed unnecessary return statements , renamed variables to make the code more understandable.

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.thealgorithms.sorts;

public class SmoothSortTest extends SortingAlgorithmTest {
@Override
SortAlgorithm getSortAlgorithm() {
return new SmoothSort();
}
}