├── img ├── big-o-cheat-sheet-pdf-en-transp_936.png ├── Java_Versions_PDF_Cheat_Sheet_Mockup_936.png └── mastering-data-structures-product-mockup-cropped-1600.png ├── .gitignore ├── checkstyle-suppressions.xml ├── src ├── test │ └── java │ │ └── eu │ │ └── happycoders │ │ └── sort │ │ └── method │ │ ├── InsertionSortTest.java │ │ ├── SelectionSortTest.java │ │ ├── JavaArraysSortTest.java │ │ ├── heapsort │ │ ├── HeapsortBuildHeapAndHeapifyTest.java │ │ ├── BottomUpBuildHeapAndHeapifyTest.java │ │ ├── HeapsortTest.java │ │ ├── BottomUpHeapsortTest.java │ │ ├── HeapsortSlowComparisonsTest.java │ │ ├── BottomUpHeapsortSlowComparisonsTest.java │ │ └── BuildHeapAndHeapifyTest.java │ │ ├── bubblesort │ │ ├── BubbleSortTest.java │ │ ├── BubbleSortOpt1Test.java │ │ ├── BubbleSortOpt2Test.java │ │ ├── BubbleSortParallelOddEvenTest.java │ │ └── BubbleSortParallelDivideAndConquerTest.java │ │ ├── quicksort │ │ ├── QuicksortSimpleTest.java │ │ ├── QuicksortVariant1LeftPivotTest.java │ │ ├── QuicksortVariant2LeftPivotTest.java │ │ ├── QuicksortVariant3LeftPivotTest.java │ │ ├── QuicksortVariant1RightPivotTest.java │ │ ├── QuicksortVariant2RightPivotTest.java │ │ ├── QuicksortVariant3RightPivotTest.java │ │ ├── QuicksortVariant1Median3PivotTest.java │ │ ├── QuicksortVariant1MiddlePivotTest.java │ │ ├── QuicksortVariant1RandomPivotTest.java │ │ ├── QuicksortVariant2MiddlePivotTest.java │ │ ├── QuicksortVariant2RandomPivotTest.java │ │ ├── QuicksortVariant3MiddlePivotTest.java │ │ ├── QuicksortVariant3RandomPivotTest.java │ │ ├── DualPivotQuicksortThirdsTest.java │ │ ├── QuicksortVariant2Median3PivotTest.java │ │ ├── QuicksortVariant3Median3PivotTest.java │ │ ├── QuicksortImprovedVariant1MiddlePivotTest.java │ │ ├── QuicksortImprovedVariant2MiddlePivotTest.java │ │ ├── QuicksortImprovedVariant3MiddlePivotTest.java │ │ ├── QuicksortImprovedVariant1Median3PivotTest.java │ │ ├── QuicksortImprovedVariant2Median3PivotTest.java │ │ ├── QuicksortImprovedVariant3Median3PivotTest.java │ │ ├── DualPivotQuicksortLeftRightTest.java │ │ ├── DualPivotQuicksortImprovedThirdsTest.java │ │ ├── DualPivotQuicksortImprovedLeftRightTest.java │ │ ├── DualPivotQuicksortPartitionTest.java │ │ ├── QuicksortVariant1PartitionTest.java │ │ ├── QuicksortVariant2PartitionTest.java │ │ └── QuicksortVariant3PartitionTest.java │ │ ├── SortTestLargeArray.java │ │ ├── mergesort │ │ ├── MergeSort2Test.java │ │ ├── MergeSort3Test.java │ │ ├── MergeSortTest.java │ │ ├── InPlaceMergeSortTest.java │ │ ├── NaturalMergeSortTest.java │ │ ├── MergeSortMergeTest.java │ │ ├── MergeSort3MergeTest.java │ │ ├── MergeSort2MergeTest.java │ │ └── InPlaceMergeSortMergeTest.java │ │ ├── countingsort │ │ ├── CountingSortSimpleTest.java │ │ ├── CountingSortGeneralTest.java │ │ └── CountingSortTest.java │ │ ├── radixsort │ │ ├── RadixSortWithArraysTest.java │ │ ├── RadixSortWithCountingSortTest.java │ │ ├── RadixSortWithDynamicListsTest.java │ │ ├── ParallelRadixSortWithArraysSortTest.java │ │ ├── RecursiveMsdRadixSortWithArraysTest.java │ │ ├── RadixSortWithArraysAndCustomBase2Test.java │ │ ├── RadixSortWithArraysAndCustomBase8Test.java │ │ ├── RadixSortWithArraysAndCustomBase10Test.java │ │ ├── RadixSortWithArraysAndCustomBase16Test.java │ │ ├── ParallelRecursiveMsdRadixSortWithArraysTest.java │ │ ├── RadixSortWithCountingSortAndCustomBase10Test.java │ │ ├── RadixSortWithCountingSortAndCustomBase16Test.java │ │ ├── RadixSortWithCountingSortAndCustomBase2Test.java │ │ ├── RadixSortWithCountingSortAndCustomBase8Test.java │ │ ├── RadixSortWithDynamicListsAndCustomBase10Test.java │ │ ├── RadixSortWithDynamicListsAndCustomBase16Test.java │ │ ├── RadixSortWithDynamicListsAndCustomBase2Test.java │ │ ├── RadixSortWithDynamicListsAndCustomBase8Test.java │ │ ├── RecursiveMsdRadixSortWithArraysAndCustomBase2Test.java │ │ ├── RecursiveMsdRadixSortWithArraysAndCustomBase8Test.java │ │ ├── RecursiveMsdRadixSortWithArraysAndCustomBase10Test.java │ │ ├── RecursiveMsdRadixSortWithArraysAndCustomBase16Test.java │ │ └── RadixSortHelperTest.java │ │ └── SortTest.java └── main │ └── java │ └── eu │ └── happycoders │ └── sort │ ├── method │ ├── quicksort │ │ ├── PivotStrategy.java │ │ ├── QuicksortVariant1.java │ │ ├── PivotHelper.java │ │ ├── QuicksortImproved.java │ │ ├── DualPivotQuicksortImproved.java │ │ └── QuicksortSimple.java │ ├── JavaArraysSort.java │ ├── radixsort │ │ ├── RadixSortHelper.java │ │ ├── ParallelRadixSortHelper.java │ │ ├── RadixSortWithCountingSort.java │ │ ├── RadixSortWithDynamicLists.java │ │ ├── RadixSortWithCountingSortAndCustomBase.java │ │ ├── RadixSortWithDynamicListsAndCustomBase.java │ │ ├── RadixSortWithArrays.java │ │ ├── RadixSortWithArraysAndCustomBase.java │ │ ├── RecursiveMsdRadixSortWithArrays.java │ │ ├── ParallelRecursiveMsdRadixSortWithArrays.java │ │ └── RecursiveMsdRadixSortWithArraysAndCustomBase.java │ ├── PartitioningAlgorithm.java │ ├── bubblesort │ │ ├── BubbleSortParallelOddEven.java │ │ ├── BubbleSortParallelDivideAndConquer.java │ │ ├── BubbleSort.java │ │ ├── BubbleSortOpt1.java │ │ ├── BubbleSortOpt2.java │ │ └── BubbleSortParallel.java │ ├── countingsort │ │ ├── CountingSortGeneral.java │ │ ├── CountingSortSimple.java │ │ └── CountingSort.java │ ├── heapsort │ │ ├── BottomUpHeapsortSlowComparisons.java │ │ ├── HeapsortSlowComparisons.java │ │ └── BottomUpHeapsort.java │ ├── InsertionSort.java │ ├── SelectionSort.java │ ├── SortAlgorithm.java │ ├── Counters.java │ └── mergesort │ │ ├── NaturalMergeSort.java │ │ ├── InPlaceMergeSort.java │ │ ├── MergeSort3.java │ │ └── MergeSort2.java │ ├── utils │ └── NotImplementedException.java │ └── demos │ ├── comparisons │ ├── CompareMergeSorts.java │ ├── CompareBubbleSorts.java │ ├── CompareImprovedDualPivotQuicksort.java │ ├── CompareQuicksorts.java │ ├── CompareRadixSorts.java │ └── CompareImprovedQuicksort.java │ ├── FindMinimumTest.java │ ├── pivot │ ├── PivotScorecard.java │ └── PivotComparator.java │ ├── Scorecard.java │ └── CountOperations.java ├── .github ├── dependabot.yml └── workflows │ ├── dependabot-auto-merge.yml │ └── build.yml ├── spotbugs-exclude.xml ├── results └── 2020-05-30 │ ├── Test_Results_Selection_Sort.txt │ ├── Test_Results_Bubble_Sort.txt │ └── Test_Results_Insertion_Sort.txt └── ruleset.xml /img/big-o-cheat-sheet-pdf-en-transp_936.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SvenWoltmann/sorting-algorithms-ultimate-guide/HEAD/img/big-o-cheat-sheet-pdf-en-transp_936.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .classpath 2 | .idea 3 | .project 4 | .settings 5 | out 6 | target 7 | **/gitignore 8 | *.iml 9 | *.class 10 | *.bin 11 | src/eu/happycoders/cds/sandbox 12 | -------------------------------------------------------------------------------- /img/Java_Versions_PDF_Cheat_Sheet_Mockup_936.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SvenWoltmann/sorting-algorithms-ultimate-guide/HEAD/img/Java_Versions_PDF_Cheat_Sheet_Mockup_936.png -------------------------------------------------------------------------------- /img/mastering-data-structures-product-mockup-cropped-1600.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SvenWoltmann/sorting-algorithms-ultimate-guide/HEAD/img/mastering-data-structures-product-mockup-cropped-1600.png -------------------------------------------------------------------------------- /checkstyle-suppressions.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/InsertionSortTest.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method; 2 | 3 | class InsertionSortTest extends SortTest { 4 | 5 | @Override 6 | protected SortAlgorithm getSortAlgorithm() { 7 | return new InsertionSort(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/SelectionSortTest.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method; 2 | 3 | class SelectionSortTest extends SortTest { 4 | 5 | @Override 6 | protected SortAlgorithm getSortAlgorithm() { 7 | return new SelectionSort(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/JavaArraysSortTest.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method; 2 | 3 | class JavaArraysSortTest extends SortTest { 4 | 5 | @Override 6 | protected SortAlgorithm getSortAlgorithm() { 7 | return new JavaArraysSort(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/heapsort/HeapsortBuildHeapAndHeapifyTest.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.heapsort; 2 | 3 | class HeapsortBuildHeapAndHeapifyTest extends BuildHeapAndHeapifyTest { 4 | 5 | @Override 6 | protected Heapsort getSortAlgorithm() { 7 | return new Heapsort(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/bubblesort/BubbleSortTest.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.bubblesort; 2 | 3 | import eu.happycoders.sort.method.*; 4 | 5 | class BubbleSortTest extends SortTest { 6 | 7 | @Override 8 | protected SortAlgorithm getSortAlgorithm() { 9 | return new BubbleSort(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/heapsort/BottomUpBuildHeapAndHeapifyTest.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.heapsort; 2 | 3 | class BottomUpBuildHeapAndHeapifyTest extends BuildHeapAndHeapifyTest { 4 | 5 | @Override 6 | protected Heapsort getSortAlgorithm() { 7 | return new BottomUpHeapsort(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/bubblesort/BubbleSortOpt1Test.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.bubblesort; 2 | 3 | import eu.happycoders.sort.method.*; 4 | 5 | class BubbleSortOpt1Test extends SortTest { 6 | 7 | @Override 8 | protected SortAlgorithm getSortAlgorithm() { 9 | return new BubbleSortOpt1(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/bubblesort/BubbleSortOpt2Test.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.bubblesort; 2 | 3 | import eu.happycoders.sort.method.*; 4 | 5 | class BubbleSortOpt2Test extends SortTest { 6 | 7 | @Override 8 | protected SortAlgorithm getSortAlgorithm() { 9 | return new BubbleSortOpt2(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/quicksort/QuicksortSimpleTest.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.quicksort; 2 | 3 | import eu.happycoders.sort.method.*; 4 | 5 | class QuicksortSimpleTest extends SortTest { 6 | 7 | @Override 8 | protected SortAlgorithm getSortAlgorithm() { 9 | return new QuicksortSimple(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | # Enable version updates for GitHub Actions 4 | - package-ecosystem: "github-actions" 5 | directory: "/" 6 | schedule: 7 | interval: "daily" 8 | 9 | # Enable version updates for Maven 10 | - package-ecosystem: "maven" 11 | directory: "/" 12 | schedule: 13 | interval: "daily" 14 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/SortTestLargeArray.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method; 2 | 3 | import java.util.concurrent.ThreadLocalRandom; 4 | 5 | public abstract class SortTestLargeArray extends SortTest { 6 | 7 | @Override 8 | protected int randomSize() { 9 | return ThreadLocalRandom.current().nextInt(2, 10_000); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/sort/method/quicksort/PivotStrategy.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.quicksort; 2 | 3 | /** 4 | * Strategy for calculating the pivot position. 5 | * 6 | * @author Sven Woltmann 7 | */ 8 | public enum PivotStrategy { 9 | RANDOM, 10 | LEFT, 11 | RIGHT, 12 | MIDDLE, 13 | MEDIAN3 14 | } 15 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/bubblesort/BubbleSortParallelOddEvenTest.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.bubblesort; 2 | 3 | import eu.happycoders.sort.method.*; 4 | 5 | class BubbleSortParallelOddEvenTest extends SortTest { 6 | 7 | @Override 8 | protected SortAlgorithm getSortAlgorithm() { 9 | return new BubbleSortParallelOddEven(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/heapsort/HeapsortTest.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.heapsort; 2 | 3 | import eu.happycoders.sort.method.SortAlgorithm; 4 | import eu.happycoders.sort.method.SortTest; 5 | 6 | class HeapsortTest extends SortTest { 7 | 8 | @Override 9 | protected SortAlgorithm getSortAlgorithm() { 10 | return new Heapsort(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/quicksort/QuicksortVariant1LeftPivotTest.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.quicksort; 2 | 3 | import eu.happycoders.sort.method.*; 4 | 5 | class QuicksortVariant1LeftPivotTest extends SortTest { 6 | 7 | @Override 8 | protected SortAlgorithm getSortAlgorithm() { 9 | return new QuicksortVariant1(PivotStrategy.LEFT); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/quicksort/QuicksortVariant2LeftPivotTest.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.quicksort; 2 | 3 | import eu.happycoders.sort.method.*; 4 | 5 | class QuicksortVariant2LeftPivotTest extends SortTest { 6 | 7 | @Override 8 | protected SortAlgorithm getSortAlgorithm() { 9 | return new QuicksortVariant2(PivotStrategy.LEFT); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/quicksort/QuicksortVariant3LeftPivotTest.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.quicksort; 2 | 3 | import eu.happycoders.sort.method.*; 4 | 5 | class QuicksortVariant3LeftPivotTest extends SortTest { 6 | 7 | @Override 8 | protected SortAlgorithm getSortAlgorithm() { 9 | return new QuicksortVariant3(PivotStrategy.LEFT); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/quicksort/QuicksortVariant1RightPivotTest.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.quicksort; 2 | 3 | import eu.happycoders.sort.method.*; 4 | 5 | class QuicksortVariant1RightPivotTest extends SortTest { 6 | 7 | @Override 8 | protected SortAlgorithm getSortAlgorithm() { 9 | return new QuicksortVariant1(PivotStrategy.RIGHT); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/quicksort/QuicksortVariant2RightPivotTest.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.quicksort; 2 | 3 | import eu.happycoders.sort.method.*; 4 | 5 | class QuicksortVariant2RightPivotTest extends SortTest { 6 | 7 | @Override 8 | protected SortAlgorithm getSortAlgorithm() { 9 | return new QuicksortVariant2(PivotStrategy.RIGHT); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/quicksort/QuicksortVariant3RightPivotTest.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.quicksort; 2 | 3 | import eu.happycoders.sort.method.*; 4 | 5 | class QuicksortVariant3RightPivotTest extends SortTest { 6 | 7 | @Override 8 | protected SortAlgorithm getSortAlgorithm() { 9 | return new QuicksortVariant3(PivotStrategy.RIGHT); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/bubblesort/BubbleSortParallelDivideAndConquerTest.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.bubblesort; 2 | 3 | import eu.happycoders.sort.method.*; 4 | 5 | class BubbleSortParallelDivideAndConquerTest extends SortTest { 6 | 7 | @Override 8 | protected SortAlgorithm getSortAlgorithm() { 9 | return new BubbleSortParallelDivideAndConquer(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/heapsort/BottomUpHeapsortTest.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.heapsort; 2 | 3 | import eu.happycoders.sort.method.SortAlgorithm; 4 | import eu.happycoders.sort.method.SortTest; 5 | 6 | class BottomUpHeapsortTest extends SortTest { 7 | 8 | @Override 9 | protected SortAlgorithm getSortAlgorithm() { 10 | return new BottomUpHeapsort(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/mergesort/MergeSort2Test.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.mergesort; 2 | 3 | import eu.happycoders.sort.method.SortAlgorithm; 4 | import eu.happycoders.sort.method.SortTestLargeArray; 5 | 6 | class MergeSort2Test extends SortTestLargeArray { 7 | 8 | @Override 9 | protected SortAlgorithm getSortAlgorithm() { 10 | return new MergeSort2(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/mergesort/MergeSort3Test.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.mergesort; 2 | 3 | import eu.happycoders.sort.method.SortAlgorithm; 4 | import eu.happycoders.sort.method.SortTestLargeArray; 5 | 6 | class MergeSort3Test extends SortTestLargeArray { 7 | 8 | @Override 9 | protected SortAlgorithm getSortAlgorithm() { 10 | return new MergeSort3(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/mergesort/MergeSortTest.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.mergesort; 2 | 3 | import eu.happycoders.sort.method.SortAlgorithm; 4 | import eu.happycoders.sort.method.SortTestLargeArray; 5 | 6 | class MergeSortTest extends SortTestLargeArray { 7 | 8 | @Override 9 | protected SortAlgorithm getSortAlgorithm() { 10 | return new MergeSort(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/countingsort/CountingSortSimpleTest.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.countingsort; 2 | 3 | import eu.happycoders.sort.method.SortAlgorithm; 4 | import eu.happycoders.sort.method.SortTest; 5 | 6 | class CountingSortSimpleTest extends SortTest { 7 | 8 | @Override 9 | protected SortAlgorithm getSortAlgorithm() { 10 | return new CountingSortSimple(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/radixsort/RadixSortWithArraysTest.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.radixsort; 2 | 3 | import eu.happycoders.sort.method.SortAlgorithm; 4 | import eu.happycoders.sort.method.SortTest; 5 | 6 | class RadixSortWithArraysTest extends SortTest { 7 | 8 | @Override 9 | protected SortAlgorithm getSortAlgorithm() { 10 | return new RadixSortWithArrays(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/countingsort/CountingSortGeneralTest.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.countingsort; 2 | 3 | import eu.happycoders.sort.method.SortAlgorithm; 4 | import eu.happycoders.sort.method.SortTest; 5 | 6 | class CountingSortGeneralTest extends SortTest { 7 | 8 | @Override 9 | protected SortAlgorithm getSortAlgorithm() { 10 | return new CountingSortGeneral(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/heapsort/HeapsortSlowComparisonsTest.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.heapsort; 2 | 3 | import eu.happycoders.sort.method.SortAlgorithm; 4 | import eu.happycoders.sort.method.SortTest; 5 | 6 | class HeapsortSlowComparisonsTest extends SortTest { 7 | 8 | @Override 9 | protected SortAlgorithm getSortAlgorithm() { 10 | return new HeapsortSlowComparisons(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/mergesort/InPlaceMergeSortTest.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.mergesort; 2 | 3 | import eu.happycoders.sort.method.SortAlgorithm; 4 | import eu.happycoders.sort.method.SortTestLargeArray; 5 | 6 | class InPlaceMergeSortTest extends SortTestLargeArray { 7 | 8 | @Override 9 | protected SortAlgorithm getSortAlgorithm() { 10 | return new InPlaceMergeSort(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/mergesort/NaturalMergeSortTest.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.mergesort; 2 | 3 | import eu.happycoders.sort.method.SortAlgorithm; 4 | import eu.happycoders.sort.method.SortTestLargeArray; 5 | 6 | class NaturalMergeSortTest extends SortTestLargeArray { 7 | 8 | @Override 9 | protected SortAlgorithm getSortAlgorithm() { 10 | return new NaturalMergeSort(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/radixsort/RadixSortWithCountingSortTest.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.radixsort; 2 | 3 | import eu.happycoders.sort.method.SortAlgorithm; 4 | import eu.happycoders.sort.method.SortTest; 5 | 6 | class RadixSortWithCountingSortTest extends SortTest { 7 | 8 | @Override 9 | protected SortAlgorithm getSortAlgorithm() { 10 | return new RadixSortWithCountingSort(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/radixsort/RadixSortWithDynamicListsTest.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.radixsort; 2 | 3 | import eu.happycoders.sort.method.SortAlgorithm; 4 | import eu.happycoders.sort.method.SortTest; 5 | 6 | class RadixSortWithDynamicListsTest extends SortTest { 7 | 8 | @Override 9 | protected SortAlgorithm getSortAlgorithm() { 10 | return new RadixSortWithDynamicLists(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/sort/utils/NotImplementedException.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.utils; 2 | 3 | import java.io.Serial; 4 | 5 | /** 6 | * An exception to be thrown from a not implemented method. 7 | * 8 | * @author Sven Woltmann 9 | */ 10 | public class NotImplementedException extends RuntimeException { 11 | @Serial private static final long serialVersionUID = -6359230890900035322L; 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/heapsort/BottomUpHeapsortSlowComparisonsTest.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.heapsort; 2 | 3 | import eu.happycoders.sort.method.SortAlgorithm; 4 | import eu.happycoders.sort.method.SortTest; 5 | 6 | class BottomUpHeapsortSlowComparisonsTest extends SortTest { 7 | 8 | @Override 9 | protected SortAlgorithm getSortAlgorithm() { 10 | return new BottomUpHeapsortSlowComparisons(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/radixsort/ParallelRadixSortWithArraysSortTest.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.radixsort; 2 | 3 | import eu.happycoders.sort.method.SortAlgorithm; 4 | import eu.happycoders.sort.method.SortTest; 5 | 6 | class ParallelRadixSortWithArraysSortTest extends SortTest { 7 | 8 | @Override 9 | protected SortAlgorithm getSortAlgorithm() { 10 | return new ParallelRadixSortWithArrays(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/radixsort/RecursiveMsdRadixSortWithArraysTest.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.radixsort; 2 | 3 | import eu.happycoders.sort.method.SortAlgorithm; 4 | import eu.happycoders.sort.method.SortTest; 5 | 6 | class RecursiveMsdRadixSortWithArraysTest extends SortTest { 7 | 8 | @Override 9 | protected SortAlgorithm getSortAlgorithm() { 10 | return new RecursiveMsdRadixSortWithArrays(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /spotbugs-exclude.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/radixsort/RadixSortWithArraysAndCustomBase2Test.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.radixsort; 2 | 3 | import eu.happycoders.sort.method.SortAlgorithm; 4 | import eu.happycoders.sort.method.SortTest; 5 | 6 | class RadixSortWithArraysAndCustomBase2Test extends SortTest { 7 | 8 | @Override 9 | protected SortAlgorithm getSortAlgorithm() { 10 | return new RadixSortWithArraysAndCustomBase(2); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/radixsort/RadixSortWithArraysAndCustomBase8Test.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.radixsort; 2 | 3 | import eu.happycoders.sort.method.SortAlgorithm; 4 | import eu.happycoders.sort.method.SortTest; 5 | 6 | class RadixSortWithArraysAndCustomBase8Test extends SortTest { 7 | 8 | @Override 9 | protected SortAlgorithm getSortAlgorithm() { 10 | return new RadixSortWithArraysAndCustomBase(8); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/radixsort/RadixSortWithArraysAndCustomBase10Test.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.radixsort; 2 | 3 | import eu.happycoders.sort.method.SortAlgorithm; 4 | import eu.happycoders.sort.method.SortTest; 5 | 6 | class RadixSortWithArraysAndCustomBase10Test extends SortTest { 7 | 8 | @Override 9 | protected SortAlgorithm getSortAlgorithm() { 10 | return new RadixSortWithArraysAndCustomBase(10); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/radixsort/RadixSortWithArraysAndCustomBase16Test.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.radixsort; 2 | 3 | import eu.happycoders.sort.method.SortAlgorithm; 4 | import eu.happycoders.sort.method.SortTest; 5 | 6 | class RadixSortWithArraysAndCustomBase16Test extends SortTest { 7 | 8 | @Override 9 | protected SortAlgorithm getSortAlgorithm() { 10 | return new RadixSortWithArraysAndCustomBase(16); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/quicksort/QuicksortVariant1Median3PivotTest.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.quicksort; 2 | 3 | import eu.happycoders.sort.method.SortAlgorithm; 4 | import eu.happycoders.sort.method.SortTestLargeArray; 5 | 6 | class QuicksortVariant1Median3PivotTest extends SortTestLargeArray { 7 | 8 | @Override 9 | protected SortAlgorithm getSortAlgorithm() { 10 | return new QuicksortVariant1(PivotStrategy.MEDIAN3); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/quicksort/QuicksortVariant1MiddlePivotTest.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.quicksort; 2 | 3 | import eu.happycoders.sort.method.SortAlgorithm; 4 | import eu.happycoders.sort.method.SortTestLargeArray; 5 | 6 | class QuicksortVariant1MiddlePivotTest extends SortTestLargeArray { 7 | 8 | @Override 9 | protected SortAlgorithm getSortAlgorithm() { 10 | return new QuicksortVariant1(PivotStrategy.MIDDLE); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/quicksort/QuicksortVariant1RandomPivotTest.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.quicksort; 2 | 3 | import eu.happycoders.sort.method.SortAlgorithm; 4 | import eu.happycoders.sort.method.SortTestLargeArray; 5 | 6 | class QuicksortVariant1RandomPivotTest extends SortTestLargeArray { 7 | 8 | @Override 9 | protected SortAlgorithm getSortAlgorithm() { 10 | return new QuicksortVariant1(PivotStrategy.RANDOM); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/quicksort/QuicksortVariant2MiddlePivotTest.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.quicksort; 2 | 3 | import eu.happycoders.sort.method.SortAlgorithm; 4 | import eu.happycoders.sort.method.SortTestLargeArray; 5 | 6 | class QuicksortVariant2MiddlePivotTest extends SortTestLargeArray { 7 | 8 | @Override 9 | protected SortAlgorithm getSortAlgorithm() { 10 | return new QuicksortVariant2(PivotStrategy.MIDDLE); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/quicksort/QuicksortVariant2RandomPivotTest.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.quicksort; 2 | 3 | import eu.happycoders.sort.method.SortAlgorithm; 4 | import eu.happycoders.sort.method.SortTestLargeArray; 5 | 6 | class QuicksortVariant2RandomPivotTest extends SortTestLargeArray { 7 | 8 | @Override 9 | protected SortAlgorithm getSortAlgorithm() { 10 | return new QuicksortVariant2(PivotStrategy.RANDOM); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/quicksort/QuicksortVariant3MiddlePivotTest.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.quicksort; 2 | 3 | import eu.happycoders.sort.method.SortAlgorithm; 4 | import eu.happycoders.sort.method.SortTestLargeArray; 5 | 6 | class QuicksortVariant3MiddlePivotTest extends SortTestLargeArray { 7 | 8 | @Override 9 | protected SortAlgorithm getSortAlgorithm() { 10 | return new QuicksortVariant3(PivotStrategy.MIDDLE); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/quicksort/QuicksortVariant3RandomPivotTest.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.quicksort; 2 | 3 | import eu.happycoders.sort.method.SortAlgorithm; 4 | import eu.happycoders.sort.method.SortTestLargeArray; 5 | 6 | class QuicksortVariant3RandomPivotTest extends SortTestLargeArray { 7 | 8 | @Override 9 | protected SortAlgorithm getSortAlgorithm() { 10 | return new QuicksortVariant3(PivotStrategy.RANDOM); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/radixsort/ParallelRecursiveMsdRadixSortWithArraysTest.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.radixsort; 2 | 3 | import eu.happycoders.sort.method.SortAlgorithm; 4 | import eu.happycoders.sort.method.SortTest; 5 | 6 | class ParallelRecursiveMsdRadixSortWithArraysTest extends SortTest { 7 | 8 | @Override 9 | protected SortAlgorithm getSortAlgorithm() { 10 | return new ParallelRecursiveMsdRadixSortWithArrays(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/radixsort/RadixSortWithCountingSortAndCustomBase10Test.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.radixsort; 2 | 3 | import eu.happycoders.sort.method.SortAlgorithm; 4 | import eu.happycoders.sort.method.SortTest; 5 | 6 | class RadixSortWithCountingSortAndCustomBase10Test extends SortTest { 7 | 8 | @Override 9 | protected SortAlgorithm getSortAlgorithm() { 10 | return new RadixSortWithCountingSortAndCustomBase(10); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/radixsort/RadixSortWithCountingSortAndCustomBase16Test.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.radixsort; 2 | 3 | import eu.happycoders.sort.method.SortAlgorithm; 4 | import eu.happycoders.sort.method.SortTest; 5 | 6 | class RadixSortWithCountingSortAndCustomBase16Test extends SortTest { 7 | 8 | @Override 9 | protected SortAlgorithm getSortAlgorithm() { 10 | return new RadixSortWithCountingSortAndCustomBase(16); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/radixsort/RadixSortWithCountingSortAndCustomBase2Test.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.radixsort; 2 | 3 | import eu.happycoders.sort.method.SortAlgorithm; 4 | import eu.happycoders.sort.method.SortTest; 5 | 6 | class RadixSortWithCountingSortAndCustomBase2Test extends SortTest { 7 | 8 | @Override 9 | protected SortAlgorithm getSortAlgorithm() { 10 | return new RadixSortWithCountingSortAndCustomBase(2); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/radixsort/RadixSortWithCountingSortAndCustomBase8Test.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.radixsort; 2 | 3 | import eu.happycoders.sort.method.SortAlgorithm; 4 | import eu.happycoders.sort.method.SortTest; 5 | 6 | class RadixSortWithCountingSortAndCustomBase8Test extends SortTest { 7 | 8 | @Override 9 | protected SortAlgorithm getSortAlgorithm() { 10 | return new RadixSortWithCountingSortAndCustomBase(8); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/radixsort/RadixSortWithDynamicListsAndCustomBase10Test.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.radixsort; 2 | 3 | import eu.happycoders.sort.method.SortAlgorithm; 4 | import eu.happycoders.sort.method.SortTest; 5 | 6 | class RadixSortWithDynamicListsAndCustomBase10Test extends SortTest { 7 | 8 | @Override 9 | protected SortAlgorithm getSortAlgorithm() { 10 | return new RadixSortWithDynamicListsAndCustomBase(10); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/radixsort/RadixSortWithDynamicListsAndCustomBase16Test.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.radixsort; 2 | 3 | import eu.happycoders.sort.method.SortAlgorithm; 4 | import eu.happycoders.sort.method.SortTest; 5 | 6 | class RadixSortWithDynamicListsAndCustomBase16Test extends SortTest { 7 | 8 | @Override 9 | protected SortAlgorithm getSortAlgorithm() { 10 | return new RadixSortWithDynamicListsAndCustomBase(10); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/radixsort/RadixSortWithDynamicListsAndCustomBase2Test.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.radixsort; 2 | 3 | import eu.happycoders.sort.method.SortAlgorithm; 4 | import eu.happycoders.sort.method.SortTest; 5 | 6 | class RadixSortWithDynamicListsAndCustomBase2Test extends SortTest { 7 | 8 | @Override 9 | protected SortAlgorithm getSortAlgorithm() { 10 | return new RadixSortWithDynamicListsAndCustomBase(2); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/radixsort/RadixSortWithDynamicListsAndCustomBase8Test.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.radixsort; 2 | 3 | import eu.happycoders.sort.method.SortAlgorithm; 4 | import eu.happycoders.sort.method.SortTest; 5 | 6 | class RadixSortWithDynamicListsAndCustomBase8Test extends SortTest { 7 | 8 | @Override 9 | protected SortAlgorithm getSortAlgorithm() { 10 | return new RadixSortWithDynamicListsAndCustomBase(8); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/radixsort/RecursiveMsdRadixSortWithArraysAndCustomBase2Test.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.radixsort; 2 | 3 | import eu.happycoders.sort.method.SortAlgorithm; 4 | import eu.happycoders.sort.method.SortTest; 5 | 6 | class RecursiveMsdRadixSortWithArraysAndCustomBase2Test extends SortTest { 7 | 8 | @Override 9 | protected SortAlgorithm getSortAlgorithm() { 10 | return new RecursiveMsdRadixSortWithArraysAndCustomBase(2); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/radixsort/RecursiveMsdRadixSortWithArraysAndCustomBase8Test.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.radixsort; 2 | 3 | import eu.happycoders.sort.method.SortAlgorithm; 4 | import eu.happycoders.sort.method.SortTest; 5 | 6 | class RecursiveMsdRadixSortWithArraysAndCustomBase8Test extends SortTest { 7 | 8 | @Override 9 | protected SortAlgorithm getSortAlgorithm() { 10 | return new RecursiveMsdRadixSortWithArraysAndCustomBase(8); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/radixsort/RecursiveMsdRadixSortWithArraysAndCustomBase10Test.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.radixsort; 2 | 3 | import eu.happycoders.sort.method.SortAlgorithm; 4 | import eu.happycoders.sort.method.SortTest; 5 | 6 | class RecursiveMsdRadixSortWithArraysAndCustomBase10Test extends SortTest { 7 | 8 | @Override 9 | protected SortAlgorithm getSortAlgorithm() { 10 | return new RecursiveMsdRadixSortWithArraysAndCustomBase(10); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/radixsort/RecursiveMsdRadixSortWithArraysAndCustomBase16Test.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.radixsort; 2 | 3 | import eu.happycoders.sort.method.SortAlgorithm; 4 | import eu.happycoders.sort.method.SortTest; 5 | 6 | class RecursiveMsdRadixSortWithArraysAndCustomBase16Test extends SortTest { 7 | 8 | @Override 9 | protected SortAlgorithm getSortAlgorithm() { 10 | return new RecursiveMsdRadixSortWithArraysAndCustomBase(16); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/quicksort/DualPivotQuicksortThirdsTest.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.quicksort; 2 | 3 | import eu.happycoders.sort.method.SortAlgorithm; 4 | import eu.happycoders.sort.method.SortTestLargeArray; 5 | import eu.happycoders.sort.method.quicksort.DualPivotQuicksort.PivotStrategy; 6 | 7 | class DualPivotQuicksortThirdsTest extends SortTestLargeArray { 8 | 9 | @Override 10 | protected SortAlgorithm getSortAlgorithm() { 11 | return new DualPivotQuicksort(PivotStrategy.THIRDS); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/quicksort/QuicksortVariant2Median3PivotTest.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.quicksort; 2 | 3 | import eu.happycoders.sort.method.*; 4 | import java.util.concurrent.ThreadLocalRandom; 5 | 6 | class QuicksortVariant2Median3PivotTest extends SortTest { 7 | 8 | @Override 9 | protected SortAlgorithm getSortAlgorithm() { 10 | return new QuicksortVariant2(PivotStrategy.MEDIAN3); 11 | } 12 | 13 | @Override 14 | protected int randomSize() { 15 | return ThreadLocalRandom.current().nextInt(2, 500_000); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/quicksort/QuicksortVariant3Median3PivotTest.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.quicksort; 2 | 3 | import eu.happycoders.sort.method.*; 4 | import java.util.concurrent.ThreadLocalRandom; 5 | 6 | class QuicksortVariant3Median3PivotTest extends SortTest { 7 | 8 | @Override 9 | protected SortAlgorithm getSortAlgorithm() { 10 | return new QuicksortVariant3(PivotStrategy.MEDIAN3); 11 | } 12 | 13 | @Override 14 | protected int randomSize() { 15 | return ThreadLocalRandom.current().nextInt(2, 500_000); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/quicksort/QuicksortImprovedVariant1MiddlePivotTest.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.quicksort; 2 | 3 | import eu.happycoders.sort.method.SortAlgorithm; 4 | import eu.happycoders.sort.method.SortTestLargeArray; 5 | import java.util.concurrent.ThreadLocalRandom; 6 | 7 | class QuicksortImprovedVariant1MiddlePivotTest extends SortTestLargeArray { 8 | 9 | @Override 10 | protected SortAlgorithm getSortAlgorithm() { 11 | int threshold = ThreadLocalRandom.current().nextInt(0, 100); 12 | return new QuicksortImproved(threshold, new QuicksortVariant1(PivotStrategy.MIDDLE)); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/quicksort/QuicksortImprovedVariant2MiddlePivotTest.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.quicksort; 2 | 3 | import eu.happycoders.sort.method.SortAlgorithm; 4 | import eu.happycoders.sort.method.SortTestLargeArray; 5 | import java.util.concurrent.ThreadLocalRandom; 6 | 7 | class QuicksortImprovedVariant2MiddlePivotTest extends SortTestLargeArray { 8 | 9 | @Override 10 | protected SortAlgorithm getSortAlgorithm() { 11 | int threshold = ThreadLocalRandom.current().nextInt(0, 100); 12 | return new QuicksortImproved(threshold, new QuicksortVariant2(PivotStrategy.MIDDLE)); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/quicksort/QuicksortImprovedVariant3MiddlePivotTest.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.quicksort; 2 | 3 | import eu.happycoders.sort.method.SortAlgorithm; 4 | import eu.happycoders.sort.method.SortTestLargeArray; 5 | import java.util.concurrent.ThreadLocalRandom; 6 | 7 | class QuicksortImprovedVariant3MiddlePivotTest extends SortTestLargeArray { 8 | 9 | @Override 10 | protected SortAlgorithm getSortAlgorithm() { 11 | int threshold = ThreadLocalRandom.current().nextInt(0, 100); 12 | return new QuicksortImproved(threshold, new QuicksortVariant3(PivotStrategy.MIDDLE)); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/quicksort/QuicksortImprovedVariant1Median3PivotTest.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.quicksort; 2 | 3 | import eu.happycoders.sort.method.SortAlgorithm; 4 | import eu.happycoders.sort.method.SortTestLargeArray; 5 | import java.util.concurrent.ThreadLocalRandom; 6 | 7 | class QuicksortImprovedVariant1Median3PivotTest extends SortTestLargeArray { 8 | 9 | @Override 10 | protected SortAlgorithm getSortAlgorithm() { 11 | int threshold = ThreadLocalRandom.current().nextInt(0, 100); 12 | return new QuicksortImproved(threshold, new QuicksortVariant1(PivotStrategy.MEDIAN3)); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/quicksort/QuicksortImprovedVariant2Median3PivotTest.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.quicksort; 2 | 3 | import eu.happycoders.sort.method.SortAlgorithm; 4 | import eu.happycoders.sort.method.SortTestLargeArray; 5 | import java.util.concurrent.ThreadLocalRandom; 6 | 7 | class QuicksortImprovedVariant2Median3PivotTest extends SortTestLargeArray { 8 | 9 | @Override 10 | protected SortAlgorithm getSortAlgorithm() { 11 | int threshold = ThreadLocalRandom.current().nextInt(0, 100); 12 | return new QuicksortImproved(threshold, new QuicksortVariant2(PivotStrategy.MEDIAN3)); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/quicksort/QuicksortImprovedVariant3Median3PivotTest.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.quicksort; 2 | 3 | import eu.happycoders.sort.method.SortAlgorithm; 4 | import eu.happycoders.sort.method.SortTestLargeArray; 5 | import java.util.concurrent.ThreadLocalRandom; 6 | 7 | class QuicksortImprovedVariant3Median3PivotTest extends SortTestLargeArray { 8 | 9 | @Override 10 | protected SortAlgorithm getSortAlgorithm() { 11 | int threshold = ThreadLocalRandom.current().nextInt(0, 100); 12 | return new QuicksortImproved(threshold, new QuicksortVariant3(PivotStrategy.MEDIAN3)); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/sort/method/JavaArraysSort.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method; 2 | 3 | import eu.happycoders.sort.utils.NotImplementedException; 4 | import java.util.Arrays; 5 | 6 | /** 7 | * Adapter for testing Arrays.sort. 8 | * 9 | * @author Sven Woltmann 10 | */ 11 | public class JavaArraysSort implements SortAlgorithm { 12 | 13 | @Override 14 | public void sort(int[] elements) { 15 | Arrays.sort(elements); 16 | } 17 | 18 | @Override 19 | public void sortWithCounters(int[] elements, Counters counters) { 20 | throw new NotImplementedException(); 21 | } 22 | 23 | @Override 24 | public boolean supportsCounting() { 25 | return false; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/quicksort/DualPivotQuicksortLeftRightTest.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.quicksort; 2 | 3 | import eu.happycoders.sort.method.*; 4 | import eu.happycoders.sort.method.quicksort.DualPivotQuicksort.PivotStrategy; 5 | import java.util.concurrent.ThreadLocalRandom; 6 | 7 | class DualPivotQuicksortLeftRightTest extends SortTest { 8 | 9 | @Override 10 | protected SortAlgorithm getSortAlgorithm() { 11 | return new DualPivotQuicksort(PivotStrategy.LEFT_RIGHT); 12 | } 13 | 14 | @Override 15 | protected int randomSize() { 16 | // Not more than 1000; going into recursion this deep, because partitioning 17 | // will always partition at the left or right side. 18 | return ThreadLocalRandom.current().nextInt(2, 1_000); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/quicksort/DualPivotQuicksortImprovedThirdsTest.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.quicksort; 2 | 3 | import eu.happycoders.sort.method.SortAlgorithm; 4 | import eu.happycoders.sort.method.SortTest; 5 | import eu.happycoders.sort.method.quicksort.DualPivotQuicksort.PivotStrategy; 6 | import java.util.concurrent.ThreadLocalRandom; 7 | 8 | class DualPivotQuicksortImprovedThirdsTest extends SortTest { 9 | 10 | @Override 11 | protected SortAlgorithm getSortAlgorithm() { 12 | return new DualPivotQuicksortImproved(64, PivotStrategy.THIRDS); 13 | } 14 | 15 | @Override 16 | protected int randomSize() { 17 | // Not more than 1000; going into recursion this deep, because partitioning 18 | // will always partition at the left or right side. 19 | return ThreadLocalRandom.current().nextInt(2, 1_000); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/radixsort/RadixSortHelperTest.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.radixsort; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; 4 | import static org.junit.jupiter.api.Assertions.assertThrowsExactly; 5 | 6 | import org.junit.jupiter.api.Test; 7 | 8 | class RadixSortHelperTest { 9 | 10 | @Test 11 | void checkIfContainsNegativesThrowsAnExceptionIfInputContainsANegativeNumber() { 12 | int[] elements = {5, -1, 8}; 13 | assertThrowsExactly( 14 | IllegalArgumentException.class, () -> RadixSortHelper.checkIfContainsNegatives(elements)); 15 | } 16 | 17 | @Test 18 | void checkIfContainsNegativesDoesNotThrowAnExceptionIfInputContainsNoNegativeNumber() { 19 | int[] elements = {0, 1, 2}; 20 | assertDoesNotThrow(() -> RadixSortHelper.checkIfContainsNegatives(elements)); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/quicksort/DualPivotQuicksortImprovedLeftRightTest.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.quicksort; 2 | 3 | import eu.happycoders.sort.method.SortAlgorithm; 4 | import eu.happycoders.sort.method.SortTest; 5 | import eu.happycoders.sort.method.quicksort.DualPivotQuicksort.PivotStrategy; 6 | import java.util.concurrent.ThreadLocalRandom; 7 | 8 | class DualPivotQuicksortImprovedLeftRightTest extends SortTest { 9 | 10 | @Override 11 | protected SortAlgorithm getSortAlgorithm() { 12 | return new DualPivotQuicksortImproved(64, PivotStrategy.LEFT_RIGHT); 13 | } 14 | 15 | @Override 16 | protected int randomSize() { 17 | // Not more than 1000; going into recursion this deep, because partitioning 18 | // will always partition at the left or right side. 19 | return ThreadLocalRandom.current().nextInt(2, 1_000); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/sort/demos/comparisons/CompareMergeSorts.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.demos.comparisons; 2 | 3 | import eu.happycoders.sort.method.SortAlgorithm; 4 | import eu.happycoders.sort.method.mergesort.MergeSort; 5 | import eu.happycoders.sort.method.mergesort.MergeSort2; 6 | import eu.happycoders.sort.method.mergesort.MergeSort3; 7 | 8 | /** 9 | * Compares the three merge sort algorithms. 10 | * 11 | * @author Sven Woltmann 12 | */ 13 | public class CompareMergeSorts extends DirectComparison { 14 | 15 | private static final int SIZE = 4_444_444; // ~600 ms for Merge Sort 16 | 17 | public static void main(String[] args) { 18 | new CompareMergeSorts().run(); 19 | } 20 | 21 | private void run() { 22 | SortAlgorithm[] algorithms = {new MergeSort(), new MergeSort2(), new MergeSort3()}; 23 | 24 | runTest(algorithms, SIZE); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/countingsort/CountingSortTest.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.countingsort; 2 | 3 | import eu.happycoders.sort.method.SortAlgorithm; 4 | import eu.happycoders.sort.method.SortTest; 5 | import eu.happycoders.sort.utils.ArrayUtils; 6 | import org.junit.jupiter.api.RepeatedTest; 7 | 8 | @SuppressWarnings("java:S2699") // The assertions are in the sortAndTestIfSorted() method 9 | class CountingSortTest extends SortTest { 10 | 11 | @RepeatedTest(100) 12 | void sort_randomNumbers_sorted() { 13 | sortAndTestIfSorted(ArrayUtils::createRandomArrayIncludingNegatives); 14 | } 15 | 16 | @RepeatedTest(100) 17 | void sort_sortedNumbers_sorted() { 18 | sortAndTestIfSorted(ArrayUtils::createSortedArrayIncludingNegatives); 19 | } 20 | 21 | @RepeatedTest(100) 22 | void sort_reverseNumbers_sorted() { 23 | sortAndTestIfSorted(ArrayUtils::createReversedArrayIncludingNegatives); 24 | } 25 | 26 | @Override 27 | protected SortAlgorithm getSortAlgorithm() { 28 | return new CountingSort(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /.github/workflows/dependabot-auto-merge.yml: -------------------------------------------------------------------------------- 1 | name: dependabot auto-merge 2 | # Using a short name here for two reasons: 3 | # 1) The "Workflows" column on the "Actions" tab is quite narrow 4 | # 2) The name is used as label on the README badge 5 | 6 | on: pull_request 7 | 8 | permissions: 9 | pull-requests: write 10 | contents: write 11 | 12 | jobs: 13 | dependabot: 14 | runs-on: ubuntu-latest 15 | if: ${{ github.actor == 'dependabot[bot]' }} 16 | steps: 17 | - name: Dependabot metadata 18 | id: metadata 19 | uses: dependabot/fetch-metadata@v2.4.0 20 | with: 21 | github-token: "${{ secrets.GITHUB_TOKEN }}" 22 | 23 | - name: Approve a PR 24 | run: gh pr review --approve "$PR_URL" 25 | env: 26 | PR_URL: ${{github.event.pull_request.html_url}} 27 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} 28 | 29 | - name: Enable auto-merge for Dependabot PRs 30 | if: ${{ steps.dependabot-metadata.outputs.update-type != 'version-update:semver-major' }} 31 | run: gh pr merge --auto --rebase "$PR_URL" 32 | env: 33 | PR_URL: ${{github.event.pull_request.html_url}} 34 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} 35 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/sort/method/radixsort/RadixSortHelper.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.radixsort; 2 | 3 | final class RadixSortHelper { 4 | 5 | private RadixSortHelper() {} 6 | 7 | static void checkIfContainsNegatives(int[] elements) { 8 | for (int element : elements) { 9 | if (element < 0) { 10 | throw new IllegalArgumentException("Negative elements are not allowed"); 11 | } 12 | } 13 | } 14 | 15 | static int getNumberOfDigits(int number) { 16 | int numberOfDigits = 1; 17 | while (number >= 10) { 18 | number /= 10; 19 | numberOfDigits++; 20 | } 21 | return numberOfDigits; 22 | } 23 | 24 | static int getNumberOfDigits(int number, int base) { 25 | int numberOfDigits = 1; 26 | while (number >= base) { 27 | number /= base; 28 | numberOfDigits++; 29 | } 30 | return numberOfDigits; 31 | } 32 | 33 | static int calculateDivisor(int digitIndex) { 34 | int divisor = 1; 35 | for (int i = 0; i < digitIndex; i++) { 36 | divisor *= 10; 37 | } 38 | return divisor; 39 | } 40 | 41 | static int calculateDivisor(int digitIndex, int base) { 42 | int divisor = 1; 43 | for (int i = 0; i < digitIndex; i++) { 44 | divisor *= base; 45 | } 46 | return divisor; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/sort/method/PartitioningAlgorithm.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method; 2 | 3 | /** 4 | * Partitioning algorithm interface; having all Quicksort algorithms implement this interface makes 5 | * it easier to compare the performance of their partitioning algorithms. 6 | * 7 | * @author Sven Woltmann 8 | */ 9 | public interface PartitioningAlgorithm extends SortAlgorithm { 10 | 11 | /** 12 | * Partitions the given elements from the left to the right position and returns the position of 13 | * the pivot element. 14 | * 15 | * @param elements the elements to partition 16 | * @param left the left position in the index 17 | * @param right the right position in the index 18 | * @return the position of the pivot element 19 | */ 20 | int partition(int[] elements, int left, int right); 21 | 22 | /** 23 | * Partitions the given elements from the left to the right position and returns the position of 24 | * the pivot element. 25 | * 26 | * @param elements the elements to partition 27 | * @param left the left position in the index 28 | * @param right the right position in the index 29 | * @return the position of the pivot element 30 | */ 31 | int partitionWithCounters(int[] elements, int left, int right, Counters counters); 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/sort/method/bubblesort/BubbleSortParallelOddEven.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.bubblesort; 2 | 3 | /** 4 | * Parallel Bubble Sort implementation using the "odd-even" algorithm. 5 | * 6 | * @author Sven Woltmann 7 | */ 8 | public class BubbleSortParallelOddEven extends BubbleSortParallel { 9 | 10 | /** 11 | * Sorts a partition of the elements. 12 | * 13 | * @param elements the elements 14 | * @param startPos the partition's start position 15 | * @param endPos the partition's end position 16 | * @param even whether it's the even or odd step of an iteration 17 | * @return whether any elements were swapped 18 | */ 19 | @Override 20 | boolean sortPartition(int[] elements, int startPos, int endPos, boolean even) { 21 | boolean swapped = false; 22 | 23 | // Odd steps 1, 3, 5, ... --> start at the first element 24 | // Even steps 2, 4, 6, ... --> start at the second element 25 | if (even) { 26 | startPos++; 27 | } 28 | 29 | for (int i = startPos; i < endPos && i < elements.length - 1; i += 2) { 30 | int left = elements[i]; 31 | int right = elements[i + 1]; 32 | if (left > right) { 33 | elements[i + 1] = left; 34 | elements[i] = right; 35 | swapped = true; 36 | } 37 | } 38 | 39 | return swapped; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/sort/demos/comparisons/CompareBubbleSorts.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.demos.comparisons; 2 | 3 | import eu.happycoders.sort.method.SortAlgorithm; 4 | import eu.happycoders.sort.method.bubblesort.BubbleSort; 5 | import eu.happycoders.sort.method.bubblesort.BubbleSortOpt1; 6 | import eu.happycoders.sort.method.bubblesort.BubbleSortOpt2; 7 | import eu.happycoders.sort.method.bubblesort.BubbleSortParallelDivideAndConquer; 8 | import eu.happycoders.sort.method.bubblesort.BubbleSortParallelOddEven; 9 | import java.util.List; 10 | 11 | /** 12 | * Compares the regular Quicksort with the improved Quicksort for various thresholds at which the 13 | * algorithm switches from Quicksort to Insertion Sort. 14 | * 15 | * @author Sven Woltmann 16 | */ 17 | public class CompareBubbleSorts extends DirectComparison { 18 | 19 | private static final int SIZE = 40_000; // ~500 ms for Bubble Sort 20 | 21 | public static void main(String[] args) { 22 | new CompareBubbleSorts().run(); 23 | } 24 | 25 | private void run() { 26 | List algorithms = 27 | List.of( 28 | new BubbleSort(), 29 | new BubbleSortOpt1(), 30 | new BubbleSortOpt2(), 31 | new BubbleSortParallelOddEven(), 32 | new BubbleSortParallelDivideAndConquer()); 33 | 34 | runTest(algorithms.toArray(SortAlgorithm[]::new), SIZE); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/sort/method/quicksort/QuicksortVariant1.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.quicksort; 2 | 3 | import eu.happycoders.sort.method.Counters; 4 | 5 | /** 6 | * Quicksort implementation for performance tests, supporting various pivot strategies. 7 | * 8 | *

Variant 1: swaps pivot element with rightmost element first 9 | * 10 | * @author Sven Woltmann 11 | */ 12 | public class QuicksortVariant1 extends QuicksortSimple { 13 | 14 | private final PivotStrategy pivotStrategy; 15 | 16 | public QuicksortVariant1(PivotStrategy pivotStrategy) { 17 | this.pivotStrategy = pivotStrategy; 18 | } 19 | 20 | @Override 21 | public String getName() { 22 | return this.getClass().getSimpleName() + "(pivot: " + pivotStrategy + ")"; 23 | } 24 | 25 | @Override 26 | public boolean isSuitableForSortedInput(int size) { 27 | return pivotStrategy != PivotStrategy.LEFT && pivotStrategy != PivotStrategy.RIGHT 28 | || size <= 2 << 12; 29 | } 30 | 31 | @Override 32 | public int partition(int[] elements, int left, int right) { 33 | PivotHelper.findPivotAndMoveRight(elements, left, right, pivotStrategy); 34 | return super.partition(elements, left, right); 35 | } 36 | 37 | @Override 38 | public int partitionWithCounters(int[] elements, int left, int right, Counters counters) { 39 | PivotHelper.findPivotAndMoveRight(elements, left, right, pivotStrategy); 40 | return super.partitionWithCounters(elements, left, right, counters); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/mergesort/MergeSortMergeTest.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.mergesort; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertArrayEquals; 4 | 5 | import eu.happycoders.sort.utils.ArrayUtils; 6 | import java.util.Arrays; 7 | import java.util.concurrent.ThreadLocalRandom; 8 | import org.junit.jupiter.api.*; 9 | 10 | class MergeSortMergeTest { 11 | 12 | @Test 13 | void merge_twoSortedElements_merged() { 14 | testMerge(new int[] {8}, new int[] {15}); 15 | } 16 | 17 | @Test 18 | void merge_twoUnsortedElements_merged() { 19 | testMerge(new int[] {17}, new int[] {4}); 20 | } 21 | 22 | @RepeatedTest(100) 23 | void merge_twoRandomSectionsOnAList_merged() { 24 | ThreadLocalRandom rand = ThreadLocalRandom.current(); 25 | int[] leftArray = ArrayUtils.createRandomArray(rand.nextInt(1, 1000)); 26 | Arrays.sort(leftArray); 27 | int[] rightArray = ArrayUtils.createRandomArray(rand.nextInt(1, 1000)); 28 | Arrays.sort(rightArray); 29 | testMerge(leftArray, rightArray); 30 | } 31 | 32 | private void testMerge(int[] leftArray, int[] rightArray) { 33 | int[] merged = new MergeSort().merge(leftArray, rightArray); 34 | 35 | int[] expectedArray = new int[leftArray.length + rightArray.length]; 36 | System.arraycopy(leftArray, 0, expectedArray, 0, leftArray.length); 37 | System.arraycopy(rightArray, 0, expectedArray, leftArray.length, rightArray.length); 38 | Arrays.sort(expectedArray); 39 | assertArrayEquals(expectedArray, merged); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | build-jvm: 11 | name: Build 12 | 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - name: Checkout code 17 | uses: actions/checkout@v6 18 | with: 19 | fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis 20 | 21 | - name: Set up JDK 17 22 | uses: actions/setup-java@v5 23 | with: 24 | distribution: 'adopt' 25 | java-version: 17 26 | 27 | - name: Cache SonarCloud packages 28 | uses: actions/cache@v5 29 | with: 30 | path: ~/.sonar/cache 31 | key: ${{ runner.os }}-sonar 32 | restore-keys: ${{ runner.os }}-sonar 33 | 34 | - name: Cache Maven packages 35 | uses: actions/cache@v5 36 | with: 37 | path: ~/.m2 38 | key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} 39 | restore-keys: ${{ runner.os }}-m2 40 | 41 | - name: Verify code format 42 | run: mvn -B spotless:check 43 | 44 | - name: Compile, test and verify 45 | run: mvn -B verify -Ptest-coverage,code-analysis 46 | 47 | - name: Analyze code with Sonar 48 | if: ${{ env.SONAR_TOKEN }} # the token is not available in Dependabot-triggered builds 49 | env: 50 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 51 | SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} 52 | run: mvn -B org.sonarsource.scanner.maven:sonar-maven-plugin:sonar 53 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/mergesort/MergeSort3MergeTest.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.mergesort; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertArrayEquals; 4 | 5 | import eu.happycoders.sort.utils.ArrayUtils; 6 | import java.util.Arrays; 7 | import java.util.concurrent.ThreadLocalRandom; 8 | import org.junit.jupiter.api.*; 9 | 10 | class MergeSort3MergeTest { 11 | 12 | @Test 13 | void merge_twoSortedElements_merged() { 14 | testMerge(new int[] {8}, new int[] {15}); 15 | } 16 | 17 | @Test 18 | void merge_twoUnsortedElements_merged() { 19 | testMerge(new int[] {17}, new int[] {4}); 20 | } 21 | 22 | @RepeatedTest(100) 23 | void merge_twoRandomSectionsOnAList_merged() { 24 | ThreadLocalRandom rand = ThreadLocalRandom.current(); 25 | int[] leftArray = ArrayUtils.createRandomArray(rand.nextInt(1, 1000)); 26 | Arrays.sort(leftArray); 27 | int[] rightArray = ArrayUtils.createRandomArray(rand.nextInt(1, 1000)); 28 | Arrays.sort(rightArray); 29 | testMerge(leftArray, rightArray); 30 | } 31 | 32 | private void testMerge(int[] leftArray, int[] rightArray) { 33 | int[] merged = new int[leftArray.length + rightArray.length]; 34 | new MergeSort3().merge(merged, leftArray, rightArray); 35 | 36 | int[] expectedArray = new int[leftArray.length + rightArray.length]; 37 | System.arraycopy(leftArray, 0, expectedArray, 0, leftArray.length); 38 | System.arraycopy(rightArray, 0, expectedArray, leftArray.length, rightArray.length); 39 | Arrays.sort(expectedArray); 40 | assertArrayEquals(expectedArray, merged); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/mergesort/MergeSort2MergeTest.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.mergesort; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertArrayEquals; 4 | 5 | import eu.happycoders.sort.utils.ArrayUtils; 6 | import java.util.Arrays; 7 | import java.util.concurrent.ThreadLocalRandom; 8 | import org.junit.jupiter.api.*; 9 | 10 | class MergeSort2MergeTest { 11 | 12 | @Test 13 | void merge_twoSortedElements_merged() { 14 | testMerge(new int[] {8, 15}); 15 | } 16 | 17 | @Test 18 | void merge_twoUnsortedElements_merged() { 19 | testMerge(new int[] {17, 4}); 20 | } 21 | 22 | @RepeatedTest(100) 23 | void merge_twoRandomSectionsOnAList_merged() { 24 | int length = ThreadLocalRandom.current().nextInt(2, 20); 25 | int[] numbers = ArrayUtils.createRandomArray(length); 26 | testMerge(numbers); 27 | } 28 | 29 | private void testMerge(int[] numbers) { 30 | ThreadLocalRandom rand = ThreadLocalRandom.current(); 31 | int length = numbers.length; 32 | int left = rand.nextInt(0, length / 2); 33 | int right = rand.nextInt(length / 2, length); 34 | int mid = left + (right - left) / 2; 35 | 36 | int[] arrayBefore = Arrays.copyOfRange(numbers, left, right + 1); 37 | Arrays.sort(numbers, left, mid + 1); 38 | Arrays.sort(numbers, mid + 1, right + 1); 39 | new MergeSort2().merge(numbers, left, mid, right); 40 | int[] arrayAfter = Arrays.copyOfRange(numbers, left, right + 1); 41 | 42 | int[] expectedArray = arrayBefore.clone(); 43 | Arrays.sort(expectedArray); 44 | assertArrayEquals(expectedArray, arrayAfter); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/mergesort/InPlaceMergeSortMergeTest.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.mergesort; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertArrayEquals; 4 | 5 | import eu.happycoders.sort.utils.ArrayUtils; 6 | import java.util.Arrays; 7 | import java.util.concurrent.ThreadLocalRandom; 8 | import org.junit.jupiter.api.RepeatedTest; 9 | import org.junit.jupiter.api.Test; 10 | 11 | class InPlaceMergeSortMergeTest { 12 | 13 | @Test 14 | void merge_twoSortedElements_merged() { 15 | testMerge(new int[] {8, 15}); 16 | } 17 | 18 | @Test 19 | void merge_twoUnsortedElements_merged() { 20 | testMerge(new int[] {17, 4}); 21 | } 22 | 23 | @RepeatedTest(100) 24 | void merge_twoRandomSectionsOnAList_merged() { 25 | int length = ThreadLocalRandom.current().nextInt(2, 20); 26 | int[] numbers = ArrayUtils.createRandomArray(length); 27 | testMerge(numbers); 28 | } 29 | 30 | private void testMerge(int[] numbers) { 31 | ThreadLocalRandom rand = ThreadLocalRandom.current(); 32 | int length = numbers.length; 33 | int left = rand.nextInt(0, length / 2); 34 | int right = rand.nextInt(length / 2, length); 35 | int mid = left + (right - left) / 2; 36 | 37 | int[] arrayBefore = Arrays.copyOfRange(numbers, left, right + 1); 38 | Arrays.sort(numbers, left, mid + 1); 39 | Arrays.sort(numbers, mid + 1, right + 1); 40 | new InPlaceMergeSort().merge(numbers, left, mid + 1, right); 41 | int[] arrayAfter = Arrays.copyOfRange(numbers, left, right + 1); 42 | 43 | int[] expectedArray = arrayBefore.clone(); 44 | Arrays.sort(expectedArray); 45 | assertArrayEquals(expectedArray, arrayAfter); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/sort/method/bubblesort/BubbleSortParallelDivideAndConquer.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.bubblesort; 2 | 3 | /** 4 | * Parallel Bubble Sort implementation using a divide-and-conquer approach. 5 | * 6 | * @author Sven Woltmann 7 | */ 8 | public class BubbleSortParallelDivideAndConquer extends BubbleSortParallel { 9 | 10 | /** 11 | * Sorts a partition of the elements. 12 | * 13 | * @param elements the elements 14 | * @param startPos the partition's start position 15 | * @param endPos the partition's end position 16 | * @param even whether it's the even or odd step of an iteration 17 | * @return whether any elements were swapped 18 | */ 19 | @Override 20 | boolean sortPartition(int[] elements, int startPos, int endPos, boolean even) { 21 | boolean swapped = false; 22 | 23 | // Step 1, 3, 5, ... 24 | // iterate over all elements of the partition 25 | if (!even) { 26 | for (int i = startPos; i < endPos - 1; i++) { 27 | int left = elements[i]; 28 | int right = elements[i + 1]; 29 | if (left > right) { 30 | elements[i + 1] = left; 31 | elements[i] = right; 32 | swapped = true; 33 | } 34 | } 35 | } 36 | 37 | // Step 2, 4, 6, ... 38 | // compare this partition's last element with the next partition's first 39 | else if (endPos < elements.length - 1) { 40 | int left = elements[endPos - 1]; 41 | int right = elements[endPos]; 42 | if (left > right) { 43 | elements[endPos] = left; 44 | elements[endPos - 1] = right; 45 | swapped = true; 46 | } 47 | } 48 | 49 | return swapped; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/sort/method/bubblesort/BubbleSort.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.bubblesort; 2 | 3 | import eu.happycoders.sort.method.Counters; 4 | import eu.happycoders.sort.method.SortAlgorithm; 5 | 6 | /** 7 | * Bubble Sort implementation for performance tests. 8 | * 9 | *

Unoptimized variant. 10 | * 11 | * @author Sven Woltmann 12 | */ 13 | public class BubbleSort implements SortAlgorithm { 14 | 15 | @Override 16 | public void sort(int[] elements) { 17 | int numElements = elements.length; 18 | for (; ; ) { 19 | boolean swapped = false; 20 | for (int i = 0; i < numElements - 1; i++) { 21 | int left = elements[i]; 22 | int right = elements[i + 1]; 23 | if (left > right) { 24 | elements[i + 1] = left; 25 | elements[i] = right; 26 | swapped = true; 27 | } 28 | } 29 | if (!swapped) { 30 | break; 31 | } 32 | } 33 | } 34 | 35 | @Override 36 | public void sortWithCounters(int[] elements, Counters counters) { 37 | int numElements = elements.length; 38 | for (; ; ) { 39 | counters.incIterations(); 40 | 41 | boolean swapped = false; 42 | for (int i = 0; i < numElements - 1; i++) { 43 | counters.incIterations(); 44 | 45 | int left = elements[i]; 46 | int right = elements[i + 1]; 47 | counters.addReads(2); 48 | counters.incComparisons(); 49 | if (left > right) { 50 | elements[i + 1] = left; 51 | elements[i] = right; 52 | counters.addWrites(2); 53 | swapped = true; 54 | } 55 | } 56 | if (!swapped) { 57 | break; 58 | } 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/sort/demos/comparisons/CompareImprovedDualPivotQuicksort.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.demos.comparisons; 2 | 3 | import eu.happycoders.sort.method.SortAlgorithm; 4 | import eu.happycoders.sort.method.quicksort.DualPivotQuicksort; 5 | import eu.happycoders.sort.method.quicksort.DualPivotQuicksortImproved; 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | /** 10 | * Compares Dual-Pivot Quicksort with the improved version for various thresholds at which the 11 | * algorithm switches from Quicksort to Insertion Sort. 12 | * 13 | * @author Sven Woltmann 14 | */ 15 | public class CompareImprovedDualPivotQuicksort extends DirectComparison { 16 | 17 | private static final int SIZE = 5_555_555; // ~500 ms for Quicksort 18 | 19 | public static void main(String[] args) { 20 | new CompareImprovedDualPivotQuicksort().run(); 21 | } 22 | 23 | @SuppressWarnings("PMD.AvoidLiteralsInIfCondition") 24 | private void run() { 25 | List algorithms = new ArrayList<>(); 26 | algorithms.add(new DualPivotQuicksort(DualPivotQuicksort.PivotStrategy.THIRDS)); 27 | for (int threshold = 2; threshold < 1 << 8; threshold <<= 1) { 28 | algorithms.add( 29 | new DualPivotQuicksortImproved(threshold, DualPivotQuicksort.PivotStrategy.THIRDS)); 30 | 31 | // From 16 elements on, add threshold + threshold / 2 32 | // (... 16, 24, 32, 48, 64, 96, 128, 196) 33 | // ^^ ^^ ^^ ^^^ 34 | if (threshold >= 16) { 35 | algorithms.add( 36 | new DualPivotQuicksortImproved( 37 | threshold + threshold / 2, DualPivotQuicksort.PivotStrategy.THIRDS)); 38 | } 39 | } 40 | 41 | runTest(algorithms.toArray(SortAlgorithm[]::new), SIZE); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/sort/method/bubblesort/BubbleSortOpt1.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.bubblesort; 2 | 3 | import eu.happycoders.sort.method.Counters; 4 | import eu.happycoders.sort.method.SortAlgorithm; 5 | 6 | /** 7 | * Bubble Sort implementation for performance tests. 8 | * 9 | *

Optimized: in the n-th iteration, the n-th largest element is put into place, so we can ignore 10 | * the last n-1 elements. 11 | * 12 | * @author Sven Woltmann 13 | */ 14 | public class BubbleSortOpt1 implements SortAlgorithm { 15 | 16 | @Override 17 | public void sort(int[] elements) { 18 | for (int max = elements.length - 1; max > 0; max--) { 19 | boolean swapped = false; 20 | for (int i = 0; i < max; i++) { 21 | int left = elements[i]; 22 | int right = elements[i + 1]; 23 | if (left > right) { 24 | elements[i + 1] = left; 25 | elements[i] = right; 26 | swapped = true; 27 | } 28 | } 29 | if (!swapped) { 30 | break; 31 | } 32 | } 33 | } 34 | 35 | @Override 36 | public void sortWithCounters(int[] elements, Counters counters) { 37 | for (int max = elements.length - 1; max > 0; max--) { 38 | counters.incIterations(); 39 | 40 | boolean swapped = false; 41 | for (int i = 0; i < max; i++) { 42 | counters.incIterations(); 43 | 44 | int left = elements[i]; 45 | int right = elements[i + 1]; 46 | counters.addReads(2); 47 | counters.incComparisons(); 48 | if (left > right) { 49 | elements[i + 1] = left; 50 | elements[i] = right; 51 | counters.addWrites(2); 52 | swapped = true; 53 | } 54 | } 55 | if (!swapped) { 56 | break; 57 | } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/sort/demos/FindMinimumTest.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.demos; 2 | 3 | import eu.happycoders.sort.utils.ArrayUtils; 4 | import java.util.Locale; 5 | 6 | /** 7 | * Counts minPos/min assignments for finding the smallest element in an unsorted array. 8 | * 9 | * @author Sven Woltmann 10 | */ 11 | public class FindMinimumTest { 12 | private static final int NUM_SIZES = 29; 13 | private static final int NUM_TESTS = 100; 14 | private final int[] counts = new int[NUM_SIZES]; 15 | 16 | public static void main(String[] args) { 17 | new FindMinimumTest().run(); 18 | } 19 | 20 | private void run() { 21 | for (int i = 0; i < NUM_TESTS; i++) { 22 | test(); 23 | printResults(i + 1); 24 | } 25 | } 26 | 27 | private void test() { 28 | for (int i = 0; i < NUM_SIZES; i++) { 29 | int size = 2 << i; 30 | int assignments = countAssignmentsForSize(size); 31 | counts[i] += assignments; 32 | } 33 | } 34 | 35 | private int countAssignmentsForSize(int size) { 36 | int[] array = ArrayUtils.createRandomArray(size); 37 | int min = Integer.MAX_VALUE; 38 | int assignments = 0; 39 | for (int i = 0; i < size; i++) { 40 | int element = array[i]; 41 | if (element < min) { 42 | min = element; 43 | assignments++; 44 | } 45 | } 46 | return assignments; 47 | } 48 | 49 | @SuppressWarnings({"PMD.SystemPrintln", "java:S106"}) 50 | private void printResults(int iterations) { 51 | System.out.printf(Locale.US, "Results after %d iterations:%n", iterations); 52 | for (int i = 0; i < NUM_SIZES; i++) { 53 | int size = 2 << i; 54 | double avg = (double) counts[i] / iterations; 55 | System.out.printf(Locale.US, "- size: %,11d --> avg. no of assignments: %5.2f%n", size, avg); 56 | } 57 | System.out.println(); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/sort/method/bubblesort/BubbleSortOpt2.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.bubblesort; 2 | 3 | import eu.happycoders.sort.method.Counters; 4 | import eu.happycoders.sort.method.SortAlgorithm; 5 | 6 | /** 7 | * Bubble Sort implementation for performance tests. 8 | * 9 | *

Optimized: in each iteration, more than one element can be placed in its final position; we 10 | * assume all elements after the last swap to be sorted. 11 | * 12 | * @author Sven Woltmann 13 | */ 14 | public class BubbleSortOpt2 implements SortAlgorithm { 15 | 16 | @Override 17 | public void sort(int[] elements) { 18 | int max = elements.length - 1; 19 | for (; ; ) { 20 | int lastSwapped = 0; 21 | for (int i = 0; i < max; i++) { 22 | int left = elements[i]; 23 | int right = elements[i + 1]; 24 | if (left > right) { 25 | elements[i + 1] = left; 26 | elements[i] = right; 27 | lastSwapped = i; 28 | } 29 | } 30 | if (lastSwapped == 0) { 31 | break; 32 | } 33 | max = lastSwapped; 34 | } 35 | } 36 | 37 | @Override 38 | public void sortWithCounters(int[] elements, Counters counters) { 39 | int max = elements.length - 1; 40 | for (; ; ) { 41 | counters.incIterations(); 42 | int lastSwapped = 0; 43 | for (int i = 0; i < max; i++) { 44 | counters.incIterations(); 45 | 46 | int left = elements[i]; 47 | int right = elements[i + 1]; 48 | counters.addReads(2); 49 | counters.incComparisons(); 50 | if (left > right) { 51 | elements[i + 1] = left; 52 | elements[i] = right; 53 | counters.addWrites(2); 54 | lastSwapped = i; 55 | } 56 | } 57 | if (lastSwapped == 0) { 58 | break; 59 | } 60 | max = lastSwapped; 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/sort/method/countingsort/CountingSortGeneral.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.countingsort; 2 | 3 | import eu.happycoders.sort.method.Counters; 4 | import eu.happycoders.sort.method.SortAlgorithm; 5 | import eu.happycoders.sort.utils.NotImplementedException; 6 | 7 | /** 8 | * General Counting Sort implementation. 9 | * 10 | *

For simplicity, this implementation allows only elements >= 0. 11 | * 12 | * @author Sven Woltmann 13 | */ 14 | public class CountingSortGeneral implements SortAlgorithm { 15 | 16 | @Override 17 | public void sort(int[] elements) { 18 | int maxValue = findMax(elements); 19 | int[] counts = new int[maxValue + 1]; 20 | 21 | // Phase 1: Count 22 | for (int element : elements) { 23 | counts[element]++; 24 | } 25 | 26 | // Phase 2: Aggregate 27 | for (int i = 1; i <= maxValue; i++) { 28 | counts[i] += counts[i - 1]; 29 | } 30 | 31 | // Phase 3: Write to target array 32 | int[] target = new int[elements.length]; 33 | for (int i = elements.length - 1; i >= 0; i--) { 34 | int element = elements[i]; 35 | target[--counts[element]] = element; 36 | } 37 | 38 | // Copy target back to input array 39 | System.arraycopy(target, 0, elements, 0, elements.length); 40 | } 41 | 42 | private int findMax(int[] elements) { 43 | int max = 0; 44 | for (int element : elements) { 45 | if (element < 0) { 46 | throw new IllegalArgumentException("This implementation does not support negative values."); 47 | } 48 | if (element > max) { 49 | max = element; 50 | } 51 | } 52 | return max; 53 | } 54 | 55 | @Override 56 | public void sortWithCounters(int[] elements, Counters counters) { 57 | throw new NotImplementedException(); 58 | } 59 | 60 | @Override 61 | public boolean supportsCounting() { 62 | return false; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/sort/method/heapsort/BottomUpHeapsortSlowComparisons.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.heapsort; 2 | 3 | import eu.happycoders.sort.method.Counters; 4 | import eu.happycoders.sort.utils.NotImplementedException; 5 | 6 | /** 7 | * Bottom-up heapsort implementation with slow comparisons (to show that bottom-up heapsort is 8 | * faster than regular heapsort if comparisons are expensive). 9 | * 10 | * @author Sven Woltmann 11 | */ 12 | public class BottomUpHeapsortSlowComparisons extends BottomUpHeapsort { 13 | 14 | @Override 15 | int findLeaf(int[] heap, int length, int rootPos) { 16 | int pos = rootPos; 17 | int leftChildPos = pos * 2 + 1; 18 | int rightChildPos = pos * 2 + 2; 19 | 20 | while (rightChildPos < length) { 21 | slowDown(); 22 | if (heap[rightChildPos] > heap[leftChildPos]) { 23 | pos = rightChildPos; 24 | } else { 25 | pos = leftChildPos; 26 | } 27 | leftChildPos = pos * 2 + 1; 28 | rightChildPos = pos * 2 + 2; 29 | } 30 | 31 | if (leftChildPos < length) { 32 | pos = leftChildPos; 33 | } 34 | 35 | return pos; 36 | } 37 | 38 | @Override 39 | int findTargetNodeBottomUp(int[] heap, int rootPos, int leafPos) { 40 | int parent = heap[rootPos]; 41 | while (leafPos != rootPos) { 42 | slowDown(); 43 | if (heap[leafPos] < parent) { 44 | leafPos = getParentPos(leafPos); 45 | } else { 46 | break; 47 | } 48 | } 49 | return leafPos; 50 | } 51 | 52 | @Override 53 | public void sortWithCounters(int[] elements, Counters counters) { 54 | throw new NotImplementedException(); 55 | } 56 | 57 | private void slowDown() { 58 | Thread.onSpinWait(); 59 | } 60 | 61 | @Override 62 | public boolean isSuitableForSortedInput(int size) { 63 | return false; 64 | } 65 | 66 | @Override 67 | public boolean supportsCounting() { 68 | return false; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/sort/method/InsertionSort.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method; 2 | 3 | /** 4 | * Insertion Sort implementation for performance tests. 5 | * 6 | * @author Sven Woltmann 7 | */ 8 | // We don't want to use System.arraycopy - we want to demonstrate how the algorithm works 9 | @SuppressWarnings("PMD.AvoidArrayLoops") 10 | public class InsertionSort implements SortAlgorithm { 11 | 12 | @Override 13 | public void sort(int[] elements) { 14 | sort(elements, 0, elements.length); 15 | } 16 | 17 | public void sort(int[] elements, int fromIndex, int toIndex) { 18 | for (int i = fromIndex + 1; i < toIndex; i++) { 19 | int elementToSort = elements[i]; 20 | 21 | // Move element to the left until it's at the right position 22 | int j = i; 23 | while (j > fromIndex && elementToSort < elements[j - 1]) { 24 | elements[j] = elements[j - 1]; 25 | j--; 26 | } 27 | elements[j] = elementToSort; 28 | } 29 | } 30 | 31 | @Override 32 | public void sortWithCounters(int[] elements, Counters counters) { 33 | sortWithCounters(elements, 0, elements.length, counters); 34 | } 35 | 36 | public void sortWithCounters(int[] elements, int fromIndex, int toIndex, Counters counters) { 37 | for (int i = fromIndex + 1; i < toIndex; i++) { 38 | counters.incIterations(); 39 | 40 | int number = elements[i]; 41 | counters.incReads(); 42 | 43 | // Move element to the left until it's at the right position 44 | int j = i; 45 | while (j > fromIndex) { 46 | counters.incIterations(); 47 | 48 | counters.incComparisons(); 49 | if (number < elements[j - 1]) { 50 | elements[j] = elements[j - 1]; 51 | counters.incReads(); 52 | counters.incWrites(); 53 | j--; 54 | } else { 55 | break; 56 | } 57 | } 58 | elements[j] = number; 59 | counters.incWrites(); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/sort/demos/comparisons/CompareQuicksorts.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.demos.comparisons; 2 | 3 | import eu.happycoders.sort.method.SortAlgorithm; 4 | import eu.happycoders.sort.method.quicksort.PivotStrategy; 5 | import eu.happycoders.sort.method.quicksort.QuicksortSimple; 6 | import eu.happycoders.sort.method.quicksort.QuicksortVariant1; 7 | import eu.happycoders.sort.method.quicksort.QuicksortVariant2; 8 | import eu.happycoders.sort.method.quicksort.QuicksortVariant3; 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | /** 13 | * Compares the various Quicksort algorithm variants. 14 | * 15 | * @author Sven Woltmann 16 | */ 17 | public class CompareQuicksorts extends DirectComparison { 18 | 19 | private static final int SIZE = 5_555_555; // ~500 ms for Quicksort 20 | 21 | public static void main(String[] args) { 22 | new CompareQuicksorts().run(); 23 | } 24 | 25 | private void run() { 26 | List algorithms = new ArrayList<>(); 27 | 28 | algorithms.add(new QuicksortSimple()); 29 | 30 | algorithms.add(new QuicksortVariant1(PivotStrategy.RANDOM)); 31 | algorithms.add(new QuicksortVariant1(PivotStrategy.RIGHT)); 32 | algorithms.add(new QuicksortVariant1(PivotStrategy.MIDDLE)); 33 | algorithms.add(new QuicksortVariant1(PivotStrategy.MEDIAN3)); 34 | 35 | algorithms.add(new QuicksortVariant2(PivotStrategy.RANDOM)); 36 | algorithms.add(new QuicksortVariant2(PivotStrategy.RIGHT)); 37 | algorithms.add(new QuicksortVariant2(PivotStrategy.MIDDLE)); 38 | algorithms.add(new QuicksortVariant2(PivotStrategy.MEDIAN3)); 39 | 40 | algorithms.add(new QuicksortVariant3(PivotStrategy.RANDOM)); 41 | algorithms.add(new QuicksortVariant3(PivotStrategy.RIGHT)); 42 | algorithms.add(new QuicksortVariant3(PivotStrategy.MIDDLE)); 43 | algorithms.add(new QuicksortVariant3(PivotStrategy.MEDIAN3)); 44 | 45 | runTest(algorithms.toArray(SortAlgorithm[]::new), SIZE); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/sort/method/SelectionSort.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method; 2 | 3 | /** 4 | * Selection Sort implementation for performance tests. 5 | * 6 | * @author Sven Woltmann 7 | */ 8 | public class SelectionSort implements SortAlgorithm { 9 | 10 | @Override 11 | public void sort(int[] elements) { 12 | int length = elements.length; 13 | 14 | for (int i = 0; i < length - 1; i++) { 15 | // Search the smallest element in the remaining array 16 | int minPos = i; 17 | int min = elements[minPos]; 18 | for (int j = i + 1; j < length; j++) { 19 | if (elements[j] < min) { 20 | minPos = j; 21 | min = elements[minPos]; 22 | } 23 | } 24 | 25 | // Swap min with element at pos i 26 | if (minPos != i) { 27 | elements[minPos] = elements[i]; 28 | elements[i] = min; 29 | } 30 | } 31 | } 32 | 33 | @Override 34 | public void sortWithCounters(int[] elements, Counters counters) { 35 | int length = elements.length; 36 | 37 | for (int i = 0; i < length - 1; i++) { 38 | // Search the smallest element in the remaining array 39 | counters.incIterations(); 40 | 41 | int minPos = i; 42 | int min = elements[minPos]; 43 | counters.incReads(); 44 | counters.incLocalVariableAssignments(); 45 | 46 | for (int j = i + 1; j < length; j++) { 47 | counters.incIterations(); 48 | 49 | int numAtJ = elements[j]; 50 | counters.incReads(); 51 | 52 | counters.incComparisons(); 53 | if (numAtJ < min) { 54 | minPos = j; 55 | min = numAtJ; 56 | counters.incLocalVariableAssignments(); 57 | } 58 | } 59 | 60 | // Swap min with element at pos i 61 | if (minPos != i) { 62 | elements[minPos] = elements[i]; 63 | counters.incReads(); 64 | 65 | elements[i] = min; 66 | counters.incWrites(); 67 | } 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/sort/method/SortAlgorithm.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method; 2 | 3 | /** 4 | * Sort algorithm interface; having all sort algorithms implement this interface makes it easier to 5 | * write a test program. 6 | * 7 | *

If not for a test, I would omit the interface and make the sort methods static instead. 8 | * 9 | * @author Sven Woltmann 10 | */ 11 | public interface SortAlgorithm { 12 | 13 | void sort(int[] elements); 14 | 15 | void sortWithCounters(int[] elements, Counters counters); 16 | 17 | /** 18 | * Returns the name, which is the class name by default, but can also be overridden, e.g., in 19 | * Quicksort to include the pivot strategy. 20 | * 21 | * @return the name 22 | */ 23 | default String getName() { 24 | return this.getClass().getSimpleName(); 25 | } 26 | 27 | /** 28 | * Indicates whether this test is suitable for pre-sorted input. 29 | * 30 | *

This is, for example, not the case for Quicksort using the left-most or right-most element 31 | * as pivot element, as the recursion would be too deep and we would get a StackOverflowException. 32 | * 33 | * @param size the number of elements 34 | * @return whether this test is suitable for pre-sorted input 35 | */ 36 | default boolean isSuitableForSortedInput(int size) { 37 | return true; 38 | } 39 | 40 | /** 41 | * Indicates whether this test is suitable for the given input size. 42 | * 43 | *

CountingSort, for example, should be limited to a specific size. 44 | * 45 | * @param size the number of elements 46 | * @return whether this test is suitable for pre-sorted input 47 | */ 48 | default boolean isSuitableForInputSize(int size) { 49 | return true; 50 | } 51 | 52 | /** 53 | * Indicates whether this algorithm supports counting operations. 54 | * 55 | * @return whether this algorithm supports counting operations 56 | */ 57 | default boolean supportsCounting() { 58 | return true; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/SortTest.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertArrayEquals; 4 | import static org.junit.jupiter.api.Assertions.assertThrows; 5 | 6 | import eu.happycoders.sort.utils.ArrayUtils; 7 | import eu.happycoders.sort.utils.NotImplementedException; 8 | import java.util.Arrays; 9 | import java.util.concurrent.ThreadLocalRandom; 10 | import java.util.function.Function; 11 | import org.junit.jupiter.api.RepeatedTest; 12 | import org.junit.jupiter.api.Test; 13 | 14 | public abstract class SortTest { 15 | 16 | @RepeatedTest(100) 17 | void sort_randomNumbers_sorted() { 18 | sortAndTestIfSorted(ArrayUtils::createRandomArray); 19 | } 20 | 21 | @RepeatedTest(100) 22 | void sort_sortedNumbers_sorted() { 23 | sortAndTestIfSorted(ArrayUtils::createSortedArray); 24 | } 25 | 26 | @RepeatedTest(100) 27 | void sort_reverseNumbers_sorted() { 28 | sortAndTestIfSorted(ArrayUtils::createReversedArray); 29 | } 30 | 31 | protected void sortAndTestIfSorted(Function arraySupplier) { 32 | int[] numbers = arraySupplier.apply(randomSize()); 33 | 34 | int[] numbersCopy = numbers.clone(); 35 | Arrays.sort(numbersCopy); 36 | 37 | getSortAlgorithm().sort(numbers); 38 | 39 | assertArrayEquals(numbersCopy, numbers); 40 | } 41 | 42 | @Test 43 | void sortWithCounter_randomNumbers_eitherSortedOrNotImplementedException() { 44 | int[] numbers = ArrayUtils.createRandomArray(randomSize()); 45 | 46 | int[] numbersCopy = numbers.clone(); 47 | Arrays.sort(numbersCopy); 48 | 49 | SortAlgorithm sortAlgorithm = getSortAlgorithm(); 50 | Counters counters = new Counters(); 51 | if (sortAlgorithm.supportsCounting()) { 52 | sortAlgorithm.sortWithCounters(numbers, counters); 53 | assertArrayEquals(numbersCopy, numbers); 54 | } else { 55 | assertThrows( 56 | NotImplementedException.class, () -> sortAlgorithm.sortWithCounters(numbers, counters)); 57 | } 58 | } 59 | 60 | protected int randomSize() { 61 | return ThreadLocalRandom.current().nextInt(2, 1_000); 62 | } 63 | 64 | protected abstract SortAlgorithm getSortAlgorithm(); 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/sort/demos/pivot/PivotScorecard.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.demos.pivot; 2 | 3 | import java.util.Locale; 4 | 5 | /** 6 | * Scorecard for the {@link PivotComparator}, printing how many times the partitioning was a 7 | * specific ratio or better. 8 | * 9 | * @author Sven Woltmann 10 | */ 11 | public class PivotScorecard { 12 | 13 | private final String name; 14 | 15 | private int count; 16 | private int count60Percent; 17 | private int count67Percent; 18 | private int count75Percent; 19 | 20 | public PivotScorecard(String name) { 21 | this.name = name; 22 | } 23 | 24 | public String getName() { 25 | return name; 26 | } 27 | 28 | /** 29 | * Adds the specified larger partition percentage to the scorecard. 30 | * 31 | * @param largerPartPercentage the percentage of the larger partition compared to the number of 32 | * elements 33 | */ 34 | @SuppressWarnings("PMD.AvoidLiteralsInIfCondition") 35 | public void add(double largerPartPercentage) { 36 | if (largerPartPercentage < 50.0) { 37 | throw new IllegalArgumentException("The larger partition can not be less than 50%"); 38 | } 39 | count++; 40 | if (largerPartPercentage <= 60.0) { 41 | count60Percent++; 42 | } 43 | if (largerPartPercentage <= 66.667) { 44 | count67Percent++; 45 | } 46 | if (largerPartPercentage <= 75.0) { 47 | count75Percent++; 48 | } 49 | } 50 | 51 | /** 52 | * Prints how many times the partitioning was 1.5:1 or better, 2:1 or better, and 3:1 or better. 53 | * 54 | * @param longestNameLength the length to which the name is padded 55 | */ 56 | @SuppressWarnings({"PMD.SystemPrintln", "java:S106"}) 57 | public void printResult(int longestNameLength) { 58 | String format = 59 | "%-" 60 | + longestNameLength 61 | + "s -> " 62 | + "1.5:1 or better: %5.2f %%; 2:1 or better: %5.2f %%; " 63 | + "3:1 or better: %5.2f %%%n"; 64 | System.out.printf( 65 | Locale.US, 66 | format, 67 | name, 68 | count60Percent * 100.0 / count, 69 | count67Percent * 100.0 / count, 70 | count75Percent * 100.0 / count); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/sort/method/radixsort/ParallelRadixSortHelper.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.radixsort; 2 | 3 | final class ParallelRadixSortHelper { 4 | 5 | private ParallelRadixSortHelper() {} 6 | 7 | static Segment[] splitIntoSegments(int[] elements) { 8 | int processors = Runtime.getRuntime().availableProcessors(); 9 | 10 | // Let's put at least 10 elements into a segment, otherwise the overhead would be very high 11 | int maxProcessors = Math.max(elements.length / 10, 1); 12 | if (processors > maxProcessors) { 13 | processors = maxProcessors; 14 | } 15 | 16 | Segment[] segments = new Segment[processors]; 17 | for (int i = 0; i < processors; i++) { 18 | int start = i * elements.length / processors; 19 | int nextStart = (i + 1) * elements.length / processors; 20 | segments[i] = new Segment(start, nextStart); 21 | } 22 | 23 | return segments; 24 | } 25 | 26 | static class Segment { 27 | private final int start; 28 | private final int end; 29 | 30 | private final int[] bucketCounts = new int[10]; 31 | private final int[] bucketWritePositions = new int[10]; 32 | 33 | private Segment(int start, int end) { 34 | this.start = start; 35 | this.end = end; 36 | } 37 | 38 | int getStart() { 39 | return start; 40 | } 41 | 42 | int getEnd() { 43 | return end; 44 | } 45 | 46 | int getBucketCount(int bucketIndex) { 47 | return bucketCounts[bucketIndex]; 48 | } 49 | 50 | void setCounts(int[] counts) { 51 | System.arraycopy(counts, 0, bucketCounts, 0, 10); 52 | } 53 | 54 | int getBucketWritePosition(int bucketIndex) { 55 | return bucketWritePositions[bucketIndex]; 56 | } 57 | 58 | int getAndIncrementBucketWritePosition(int bucketIndex) { 59 | int position = bucketWritePositions[bucketIndex]; 60 | bucketWritePositions[bucketIndex]++; 61 | return position; 62 | } 63 | 64 | void setBucketWritePosition(int bucketIndex, int position) { 65 | bucketWritePositions[bucketIndex] = position; 66 | } 67 | 68 | void resetBucketWritePositions() { 69 | for (int i = 0; i < 10; i++) { 70 | bucketWritePositions[i] = 0; 71 | } 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/sort/method/heapsort/HeapsortSlowComparisons.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.heapsort; 2 | 3 | import eu.happycoders.sort.method.Counters; 4 | import eu.happycoders.sort.utils.ArrayUtils; 5 | import eu.happycoders.sort.utils.NotImplementedException; 6 | 7 | /** 8 | * Heapsort implementation with slow comparisons (to show that bottom-up heapsort is faster than 9 | * regular heapsort if comparisons are expensive). 10 | * 11 | * @author Sven Woltmann 12 | */ 13 | public class HeapsortSlowComparisons extends Heapsort { 14 | 15 | /** 16 | * "Fixes" a max heap starting at the given parent position. 17 | * 18 | * @param heap the heap to be fixed 19 | * @param length the number of elements in the array that belong to the heap 20 | * @param parentPos the parent position 21 | */ 22 | @Override 23 | void heapify(int[] heap, int length, int parentPos) { 24 | while (true) { 25 | int leftChildPos = parentPos * 2 + 1; 26 | int rightChildPos = parentPos * 2 + 2; 27 | 28 | // Find the largest element 29 | int largestPos = parentPos; 30 | if (leftChildPos < length) { 31 | slowDown(); 32 | if (heap[leftChildPos] > heap[largestPos]) { 33 | largestPos = leftChildPos; 34 | } 35 | } 36 | if (rightChildPos < length) { 37 | slowDown(); 38 | if (heap[rightChildPos] > heap[largestPos]) { 39 | largestPos = rightChildPos; 40 | } 41 | } 42 | 43 | // largestPos is now either parentPos, leftChildPos or rightChildPos. 44 | // If it's the parent, we're done 45 | if (largestPos == parentPos) { 46 | break; 47 | } 48 | 49 | // If it's not the parent, then switch! 50 | ArrayUtils.swap(heap, parentPos, largestPos); 51 | 52 | // ... and fix again starting at the child we moved the parent to 53 | parentPos = largestPos; 54 | } 55 | } 56 | 57 | @Override 58 | public void sortWithCounters(int[] elements, Counters counters) { 59 | throw new NotImplementedException(); 60 | } 61 | 62 | private void slowDown() { 63 | Thread.onSpinWait(); 64 | } 65 | 66 | @Override 67 | public boolean isSuitableForSortedInput(int size) { 68 | return false; 69 | } 70 | 71 | @Override 72 | public boolean supportsCounting() { 73 | return false; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/heapsort/BuildHeapAndHeapifyTest.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.heapsort; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertTrue; 4 | 5 | import eu.happycoders.sort.utils.ArrayUtils; 6 | import java.util.concurrent.ThreadLocalRandom; 7 | import java.util.stream.Stream; 8 | import org.junit.jupiter.api.RepeatedTest; 9 | import org.junit.jupiter.params.ParameterizedTest; 10 | import org.junit.jupiter.params.provider.Arguments; 11 | import org.junit.jupiter.params.provider.MethodSource; 12 | 13 | public abstract class BuildHeapAndHeapifyTest { 14 | 15 | static Stream arrayProvider() { 16 | return Stream.of( 17 | Arguments.of(new int[] {1}), 18 | Arguments.of(new int[] {1, 1}), 19 | Arguments.of(new int[] {1, 2}), 20 | Arguments.of(new int[] {2, 1}), 21 | Arguments.of(new int[] {0, 0, 0}), 22 | Arguments.of(new int[] {0, 0, 1}), 23 | Arguments.of(new int[] {0, 1, 1}), 24 | Arguments.of(new int[] {0, 1, 2}), 25 | Arguments.of(new int[] {1, 1, 2}), 26 | Arguments.of(new int[] {0, 1, 0}), 27 | Arguments.of(new int[] {0, 2, 1}), 28 | Arguments.of(new int[] {1, 2, 1}), 29 | Arguments.of(new int[] {1, 0, 0}), 30 | Arguments.of(new int[] {1, 1, 0}), 31 | Arguments.of(new int[] {2, 1, 0}), 32 | Arguments.of(new int[] {2, 1, 1})); 33 | } 34 | 35 | @ParameterizedTest 36 | @MethodSource("arrayProvider") 37 | void build_givenArray_isMaxHeap(int[] array) { 38 | testArray(array); 39 | } 40 | 41 | @RepeatedTest(100) 42 | void build_randomArray_isMaxHeap() { 43 | int size = ThreadLocalRandom.current().nextInt(4, 100); 44 | int[] array = ArrayUtils.createRandomArray(size); 45 | testArray(array); 46 | } 47 | 48 | private void testArray(int[] array) { 49 | getSortAlgorithm().buildHeap(array); 50 | assertIsHeap(array); 51 | } 52 | 53 | private void assertIsHeap(int[] array) { 54 | // Every element must be greater than or equal to its childs 55 | for (int i = 0; i < array.length / 2; i++) { 56 | int parent = array[i]; 57 | 58 | int leftChildPos = i * 2 + 1; 59 | if (leftChildPos < array.length) { 60 | assertTrue(parent >= array[leftChildPos]); 61 | } 62 | 63 | int rightChildPos = i * 2 + 2; 64 | if (rightChildPos < array.length) { 65 | assertTrue(parent >= array[rightChildPos]); 66 | } 67 | } 68 | } 69 | 70 | protected abstract Heapsort getSortAlgorithm(); 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/sort/demos/Scorecard.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.demos; 2 | 3 | import eu.happycoders.sort.method.SortAlgorithm; 4 | import eu.happycoders.sort.utils.ArrayUtils; 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | import java.util.Locale; 8 | 9 | /** 10 | * A scorecard to print the fastest and median times measured for a specific sort algorithm and 11 | * array size. 12 | * 13 | * @author Sven Woltmann 14 | */ 15 | public class Scorecard { 16 | 17 | private final String name; 18 | private final List times = new ArrayList<>(); 19 | 20 | private long fastest = Long.MAX_VALUE; 21 | 22 | public Scorecard(String name) { 23 | this.name = name; 24 | } 25 | 26 | public String getName() { 27 | return name; 28 | } 29 | 30 | /** 31 | * Adds a time to the scorecard. 32 | * 33 | * @param time the time 34 | * @return true if the time is a new record; false otherwise 35 | */ 36 | public boolean add(long time) { 37 | times.add(time); 38 | if (time < fastest) { 39 | fastest = time; 40 | return true; 41 | } 42 | return false; 43 | } 44 | 45 | /** 46 | * Prints the fastest and median times; followed by an optional label. 47 | * 48 | * @param longestNameLength the length to which the name is padded 49 | * @param label the optional label 50 | */ 51 | @SuppressWarnings({"PMD.SystemPrintln", "java:S106"}) 52 | public void printResult(int longestNameLength, String label) { 53 | String format = 54 | "%-" + longestNameLength + "s -> " + "fastest: %,10.3f ms, median: %,10.3f ms %s%n"; 55 | System.out.printf( 56 | Locale.US, 57 | format, 58 | name, 59 | fastest / 1_000_000.0, 60 | getMedian() / 1_000_000.0, 61 | label != null ? label : ""); 62 | } 63 | 64 | /** 65 | * Returns the median time. 66 | * 67 | * @return the median time 68 | */ 69 | public long getMedian() { 70 | int len = times.size(); 71 | long[] array = new long[len]; 72 | for (int i = 0; i < len; i++) { 73 | array[i] = times.get(i); 74 | } 75 | return ArrayUtils.median(array); 76 | } 77 | 78 | public static int findLongestAlgorithmName(SortAlgorithm[] algorithms) { 79 | int max = 0; 80 | for (SortAlgorithm algorithm : algorithms) { 81 | int nameLength = algorithm.getName().length(); 82 | if (nameLength > max) { 83 | max = nameLength; 84 | } 85 | } 86 | return max; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/sort/method/countingsort/CountingSortSimple.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.countingsort; 2 | 3 | import eu.happycoders.sort.method.Counters; 4 | import eu.happycoders.sort.method.SortAlgorithm; 5 | 6 | /** 7 | * Counting Sort implementation for performance tests. 8 | * 9 | *

For simplicity, this implementation allows only elements >= 0. 10 | * 11 | * @author Sven Woltmann 12 | */ 13 | public class CountingSortSimple implements SortAlgorithm { 14 | 15 | @Override 16 | public void sort(int[] elements) { 17 | int maxValue = findMax(elements); 18 | int[] counts = new int[maxValue + 1]; 19 | 20 | // Phase 1: Count 21 | for (int element : elements) { 22 | counts[element]++; 23 | } 24 | 25 | // Phase 2: Write results back 26 | int targetPos = 0; 27 | for (int i = 0; i < counts.length; i++) { 28 | for (int j = 0; j < counts[i]; j++) { 29 | elements[targetPos++] = i; 30 | } 31 | } 32 | } 33 | 34 | private int findMax(int[] elements) { 35 | int max = 0; 36 | for (int element : elements) { 37 | if (element < 0) { 38 | throw new IllegalArgumentException("This implementation does not support negative values."); 39 | } 40 | if (element > max) { 41 | max = element; 42 | } 43 | } 44 | return max; 45 | } 46 | 47 | @Override 48 | public void sortWithCounters(int[] elements, Counters counters) { 49 | int maxValue = findMax(elements); 50 | 51 | int length = elements.length; 52 | counters.addReads(length); 53 | counters.addIterations(length); 54 | counters.addComparisons(length); 55 | 56 | int[] counts = new int[maxValue + 1]; 57 | 58 | // Phase 1: Count 59 | counters.addIterations(length); 60 | counters.addReads(length); // read elements[i] 61 | counters.addReadsAndWrites(length); // inc counts[...] 62 | for (int i = 0; i < length; i++) { 63 | counts[elements[i]]++; 64 | } 65 | 66 | // Phase 2: Write results back 67 | Counters countersPhase2 = counters.getPhase2(); 68 | int targetPos = 0; 69 | countersPhase2.addIterations(counts.length); // outer 70 | countersPhase2.addIterations(length); // all inner combined 71 | countersPhase2.addReads(counts.length); // read counts[i] 72 | countersPhase2.addWrites(length); // write elements[targetPos++] 73 | for (int i = 0; i < counts.length; i++) { 74 | for (int j = 0; j < counts[i]; j++) { 75 | elements[targetPos++] = i; 76 | } 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/sort/demos/comparisons/CompareRadixSorts.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.demos.comparisons; 2 | 3 | import eu.happycoders.sort.method.SortAlgorithm; 4 | import eu.happycoders.sort.method.radixsort.RadixSortWithArrays; 5 | import eu.happycoders.sort.method.radixsort.RadixSortWithArraysAndCustomBase; 6 | import eu.happycoders.sort.method.radixsort.RadixSortWithCountingSort; 7 | import eu.happycoders.sort.method.radixsort.RadixSortWithCountingSortAndCustomBase; 8 | import eu.happycoders.sort.method.radixsort.RadixSortWithDynamicLists; 9 | import eu.happycoders.sort.method.radixsort.RadixSortWithDynamicListsAndCustomBase; 10 | import eu.happycoders.sort.method.radixsort.RecursiveMsdRadixSortWithArrays; 11 | import eu.happycoders.sort.method.radixsort.RecursiveMsdRadixSortWithArraysAndCustomBase; 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | import java.util.function.IntFunction; 15 | 16 | /** 17 | * Compares the various Radix Sort algorithm variants. 18 | * 19 | * @author Sven Woltmann 20 | */ 21 | public class CompareRadixSorts extends DirectComparison { 22 | 23 | private static final int SIZE = 5_555_555; 24 | private static final int MAX_BASE = 1 << 20; 25 | 26 | public static void main(String[] args) { 27 | new CompareRadixSorts().run(); 28 | } 29 | 30 | private void run() { 31 | List algorithms = new ArrayList<>(); 32 | 33 | algorithms.add(new RadixSortWithDynamicLists()); 34 | algorithms.addAll(createWithVariousBases(RadixSortWithDynamicListsAndCustomBase::new)); 35 | 36 | algorithms.add(new RadixSortWithArrays()); 37 | algorithms.addAll(createWithVariousBases(RadixSortWithArraysAndCustomBase::new)); 38 | 39 | algorithms.add(new RadixSortWithCountingSort()); 40 | algorithms.addAll(createWithVariousBases(RadixSortWithCountingSortAndCustomBase::new)); 41 | 42 | algorithms.add(new RecursiveMsdRadixSortWithArrays()); 43 | algorithms.addAll(createWithVariousBases(RecursiveMsdRadixSortWithArraysAndCustomBase::new)); 44 | 45 | runTest(algorithms.toArray(SortAlgorithm[]::new), SIZE); 46 | } 47 | 48 | private List createWithVariousBases( 49 | IntFunction algorithmConstructor) { 50 | List algorithms = new ArrayList<>(); 51 | 52 | int next10Base = 10; 53 | 54 | for (int base = 2; base <= MAX_BASE; base *= 2) { 55 | if (base > next10Base) { 56 | algorithms.add(algorithmConstructor.apply(next10Base)); 57 | next10Base *= 10; 58 | } 59 | 60 | algorithms.add(algorithmConstructor.apply(base)); 61 | } 62 | 63 | return algorithms; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/sort/method/Counters.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method; 2 | 3 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 4 | import java.util.Locale; 5 | 6 | /** 7 | * Counter for sort operations. 8 | * 9 | * @author Sven Woltmann 10 | */ 11 | @SuppressWarnings("PMD.TooManyMethods") // That's OK for this simple counter class 12 | public class Counters { 13 | 14 | private long iterations; 15 | private long comparisons; 16 | private long reads; 17 | private long writes; 18 | private long localVariableAssignments; 19 | 20 | private Counters phase2; 21 | 22 | public void incIterations() { 23 | iterations++; 24 | } 25 | 26 | public void addIterations(int increment) { 27 | iterations += increment; 28 | } 29 | 30 | public void incComparisons() { 31 | comparisons++; 32 | } 33 | 34 | public void addComparisons(int increment) { 35 | comparisons += increment; 36 | } 37 | 38 | public void incReads() { 39 | reads++; 40 | } 41 | 42 | public void addReads(int increment) { 43 | reads += increment; 44 | } 45 | 46 | public void incWrites() { 47 | writes++; 48 | } 49 | 50 | public void addWrites(int increment) { 51 | writes += increment; 52 | } 53 | 54 | public void incReadsAndWrites() { 55 | reads++; 56 | writes++; 57 | } 58 | 59 | public void addReadsAndWrites(int increment) { 60 | reads += increment; 61 | writes += increment; 62 | } 63 | 64 | public void incLocalVariableAssignments() { 65 | localVariableAssignments++; 66 | } 67 | 68 | /** 69 | * Returns a second set of counters (used by Heapsort to count operations of phase 2). Create the 70 | * second set if it doesn't exist yet. 71 | * 72 | *

Not thread-safe! 73 | * 74 | * @return the second set of counters 75 | */ 76 | @SuppressFBWarnings("EI_EXPOSE_REP") // We're intentionally exposing the "phase2" object 77 | public Counters getPhase2() { 78 | if (phase2 == null) { 79 | phase2 = new Counters(); 80 | } 81 | return phase2; 82 | } 83 | 84 | @Override 85 | public String toString() { 86 | String result = 87 | String.format( 88 | Locale.US, 89 | "iterations = %,11d, comparisons = %,11d, " 90 | + "reads = %,11d, writes = %,11d, var.assignments = %,11d", 91 | iterations, 92 | comparisons, 93 | reads, 94 | writes, 95 | localVariableAssignments); 96 | if (phase2 != null) { 97 | result += "; Phase2: " + phase2.toString(); 98 | } 99 | return result; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /results/2020-05-30/Test_Results_Selection_Sort.txt: -------------------------------------------------------------------------------- 1 | --- Results for iteration 50 for: SelectionSort (order: random) --- 2 | SelectionSort/random/1024 -> fastest: 0.155 ms, median: 0.162 ms 3 | SelectionSort/random/2048 -> fastest: 0.512 ms, median: 0.530 ms 4 | SelectionSort/random/4096 -> fastest: 1.825 ms, median: 1.882 ms 5 | SelectionSort/random/8192 -> fastest: 6.847 ms, median: 7.114 ms 6 | SelectionSort/random/16384 -> fastest: 27.239 ms, median: 27.888 ms 7 | SelectionSort/random/32768 -> fastest: 106.386 ms, median: 107.985 ms 8 | SelectionSort/random/65536 -> fastest: 425.302 ms, median: 434.026 ms 9 | SelectionSort/random/131072 -> fastest: 1,705.741 ms, median: 1,729.812 ms 10 | SelectionSort/random/262144 -> fastest: 6,843.655 ms, median: 6,913.384 ms 11 | SelectionSort/random/524288 -> fastest: 27,414.730 ms, median: 27,649.758 ms 12 | 13 | --- Results for iteration 50 for: SelectionSort (order: ascending) --- 14 | SelectionSort/ascending/1024 -> fastest: 0.111 ms, median: 0.115 ms 15 | SelectionSort/ascending/2048 -> fastest: 0.418 ms, median: 0.426 ms 16 | SelectionSort/ascending/4096 -> fastest: 1.605 ms, median: 1.645 ms 17 | SelectionSort/ascending/8192 -> fastest: 6.352 ms, median: 6.611 ms 18 | SelectionSort/ascending/16384 -> fastest: 26.036 ms, median: 26.768 ms 19 | SelectionSort/ascending/32768 -> fastest: 103.999 ms, median: 105.355 ms 20 | SelectionSort/ascending/65536 -> fastest: 417.469 ms, median: 424.329 ms 21 | SelectionSort/ascending/131072 -> fastest: 1,694.837 ms, median: 1,714.145 ms 22 | SelectionSort/ascending/262144 -> fastest: 6,820.222 ms, median: 6,880.154 ms 23 | SelectionSort/ascending/524288 -> fastest: 27,320.116 ms, median: 27,568.692 ms 24 | 25 | --- Results for iteration 50 for: SelectionSort (order: descending) --- 26 | SelectionSort/descending/1024 -> fastest: 0.265 ms, median: 0.279 ms 27 | SelectionSort/descending/2048 -> fastest: 1.006 ms, median: 1.063 ms 28 | SelectionSort/descending/4096 -> fastest: 3.976 ms, median: 4.184 ms 29 | SelectionSort/descending/8192 -> fastest: 16.022 ms, median: 16.522 ms 30 | SelectionSort/descending/16384 -> fastest: 65.071 ms, median: 65.647 ms 31 | SelectionSort/descending/32768 -> fastest: 260.293 ms, median: 265.407 ms 32 | SelectionSort/descending/65536 -> fastest: 1,043.764 ms, median: 1,052.185 ms 33 | SelectionSort/descending/131072 -> fastest: 4,172.432 ms, median: 4,209.876 ms 34 | SelectionSort/descending/262144 -> fastest: 16,787.269 ms, median: 16,863.748 ms 35 | SelectionSort/descending/524288 -> fastest: 67,219.754 ms, median: 67,537.755 ms 36 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/sort/demos/CountOperations.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.demos; 2 | 3 | import eu.happycoders.sort.method.Counters; 4 | import eu.happycoders.sort.method.SortAlgorithm; 5 | import eu.happycoders.sort.utils.ArrayUtils; 6 | import java.util.Locale; 7 | import java.util.function.IntFunction; 8 | 9 | /** 10 | * Measures the performance of all sorting algorithms for various input sizes. 11 | * 12 | * @author Sven Woltmann 13 | */ 14 | public class CountOperations { 15 | 16 | private static final int MIN_SORTING_SIZE = 1 << 3; 17 | private static final int MAX_SORTING_SIZE = 1 << 26; 18 | 19 | // Stop when counting takes longer than 20 seconds 20 | private static final int MAX_COUNTING_TIME_SECS = 20; 21 | 22 | public static void main(String[] args) { 23 | new CountOperations().run(); 24 | } 25 | 26 | private void run() { 27 | for (SortAlgorithm algorithm : UltimateTest.ALGORITHMS) { 28 | if (algorithm.supportsCounting()) { 29 | countOps(algorithm); 30 | } 31 | } 32 | } 33 | 34 | private void countOps(SortAlgorithm algorithm) { 35 | // Test with a random, a sorted, and a reversed (= sorted descending) array 36 | countOps(algorithm, false, "random", ArrayUtils::createRandomArray); 37 | countOps(algorithm, true, "ascending", ArrayUtils::createSortedArray); 38 | countOps(algorithm, true, "descending", ArrayUtils::createReversedArray); 39 | } 40 | 41 | @SuppressWarnings({"PMD.SystemPrintln", "java:S106"}) 42 | private void countOps( 43 | SortAlgorithm algorithm, 44 | boolean sorted, 45 | String inputOrder, 46 | IntFunction arraySupplier) { 47 | System.out.printf("%n--- %s (order: %s) ---%n", algorithm.getName(), inputOrder); 48 | 49 | // Sort until sorting takes more than MAX_SORTING_TIME_SECS 50 | // Upper limit used by insertion sort on already sorted data 51 | for (int size = MIN_SORTING_SIZE; 52 | size <= MAX_SORTING_SIZE 53 | && algorithm.isSuitableForInputSize(size) 54 | && (!sorted || algorithm.isSuitableForSortedInput(size)); 55 | size <<= 1) { 56 | long time = System.currentTimeMillis(); 57 | Counters counters = countOps(algorithm, arraySupplier.apply(size)); 58 | time = System.currentTimeMillis() - time; 59 | 60 | System.out.printf( 61 | Locale.US, 62 | "%s (order: %s): size = %,11d --> %s%n", 63 | algorithm.getName(), 64 | inputOrder, 65 | size, 66 | counters); 67 | 68 | // Stop after specified time 69 | if (time > MAX_COUNTING_TIME_SECS * 1_000L) { 70 | break; 71 | } 72 | } 73 | } 74 | 75 | private Counters countOps(SortAlgorithm algorithm, int[] elements) { 76 | Counters counters = new Counters(); 77 | algorithm.sortWithCounters(elements, counters); 78 | return counters; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/sort/method/radixsort/RadixSortWithCountingSort.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.radixsort; 2 | 3 | import static eu.happycoders.sort.method.radixsort.RadixSortHelper.calculateDivisor; 4 | import static eu.happycoders.sort.method.radixsort.RadixSortHelper.checkIfContainsNegatives; 5 | import static eu.happycoders.sort.method.radixsort.RadixSortHelper.getNumberOfDigits; 6 | import static eu.happycoders.sort.utils.ArrayUtils.getMaximum; 7 | 8 | import eu.happycoders.sort.method.Counters; 9 | import eu.happycoders.sort.method.SortAlgorithm; 10 | import eu.happycoders.sort.utils.NotImplementedException; 11 | 12 | /** 13 | * Radix Sort implementation using counting sort. 14 | * 15 | * @author Sven Woltmann 16 | */ 17 | public class RadixSortWithCountingSort implements SortAlgorithm { 18 | 19 | @Override 20 | public void sort(int[] elements) { 21 | checkIfContainsNegatives(elements); 22 | int max = getMaximum(elements); 23 | int numberOfDigits = getNumberOfDigits(max); 24 | 25 | // Remember input array 26 | int[] inputArray = elements; 27 | 28 | for (int digitIndex = 0; digitIndex < numberOfDigits; digitIndex++) { 29 | elements = sortByDigit(elements, digitIndex); 30 | } 31 | 32 | // Copy sorted elements back to input array 33 | System.arraycopy(elements, 0, inputArray, 0, elements.length); 34 | } 35 | 36 | private int[] sortByDigit(int[] elements, int digitIndex) { 37 | int[] counts = countDigits(elements, digitIndex); 38 | int[] prefixSums = calculatePrefixSums(counts); 39 | return collectElements(elements, digitIndex, prefixSums); 40 | } 41 | 42 | private int[] countDigits(int[] elements, int digitIndex) { 43 | int[] counts = new int[10]; 44 | int divisor = calculateDivisor(digitIndex); 45 | for (int element : elements) { 46 | int digit = element / divisor % 10; 47 | counts[digit]++; 48 | } 49 | return counts; 50 | } 51 | 52 | private int[] calculatePrefixSums(int[] counts) { 53 | int[] prefixSums = new int[10]; 54 | prefixSums[0] = counts[0]; 55 | for (int i = 1; i < 10; i++) { 56 | prefixSums[i] = prefixSums[i - 1] + counts[i]; 57 | } 58 | return prefixSums; 59 | } 60 | 61 | private int[] collectElements(int[] elements, int digitIndex, int[] prefixSums) { 62 | int divisor = calculateDivisor(digitIndex); 63 | int[] target = new int[elements.length]; 64 | for (int i = elements.length - 1; i >= 0; i--) { 65 | int element = elements[i]; 66 | int digit = element / divisor % 10; 67 | target[--prefixSums[digit]] = element; 68 | } 69 | return target; 70 | } 71 | 72 | @Override 73 | public void sortWithCounters(int[] elements, Counters counters) { 74 | throw new NotImplementedException(); 75 | } 76 | 77 | @Override 78 | public boolean supportsCounting() { 79 | return false; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/sort/demos/comparisons/CompareImprovedQuicksort.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.demos.comparisons; 2 | 3 | import eu.happycoders.sort.method.SortAlgorithm; 4 | import eu.happycoders.sort.method.quicksort.PivotStrategy; 5 | import eu.happycoders.sort.method.quicksort.QuicksortImproved; 6 | import eu.happycoders.sort.method.quicksort.QuicksortVariant1; 7 | import eu.happycoders.sort.method.quicksort.QuicksortVariant2; 8 | import eu.happycoders.sort.method.quicksort.QuicksortVariant3; 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | /** 13 | * Compares the regular Quicksort with the improved Quicksort for various thresholds at which the 14 | * algorithm switches from Quicksort to Insertion Sort. 15 | * 16 | * @author Sven Woltmann 17 | */ 18 | public class CompareImprovedQuicksort extends DirectComparison { 19 | 20 | private static final int SIZE = 5_555_555; // ~500 ms for Quicksort 21 | 22 | public static void main(String[] args) { 23 | new CompareImprovedQuicksort().run(); 24 | } 25 | 26 | @SuppressWarnings("PMD.AvoidLiteralsInIfCondition") 27 | private void run() { 28 | List algorithms = new ArrayList<>(); 29 | algorithms.add(new QuicksortVariant1(PivotStrategy.MIDDLE)); 30 | algorithms.add(new QuicksortVariant1(PivotStrategy.MEDIAN3)); 31 | algorithms.add(new QuicksortVariant2(PivotStrategy.MIDDLE)); 32 | algorithms.add(new QuicksortVariant2(PivotStrategy.MEDIAN3)); 33 | algorithms.add(new QuicksortVariant3(PivotStrategy.MIDDLE)); 34 | algorithms.add(new QuicksortVariant3(PivotStrategy.MEDIAN3)); 35 | 36 | for (int threshold = 2; threshold < 1 << 8; threshold <<= 1) { 37 | addAllVariantsForThreshold(algorithms, threshold); 38 | 39 | // From 16 elements on, add threshold + threshold / 2 40 | // (... 16, 24, 32, 48, 64, 96, 128, 196) 41 | // ^^ ^^ ^^ ^^^ 42 | if (threshold >= 16) { 43 | addAllVariantsForThreshold(algorithms, threshold + threshold / 2); 44 | } 45 | } 46 | 47 | runTest(algorithms.toArray(SortAlgorithm[]::new), SIZE); 48 | } 49 | 50 | private void addAllVariantsForThreshold(List algorithms, int threshold) { 51 | // Variant 1 52 | algorithms.add(new QuicksortImproved(threshold, new QuicksortVariant1(PivotStrategy.MIDDLE))); 53 | algorithms.add(new QuicksortImproved(threshold, new QuicksortVariant1(PivotStrategy.MEDIAN3))); 54 | 55 | // Variant 2 56 | algorithms.add(new QuicksortImproved(threshold, new QuicksortVariant2(PivotStrategy.MIDDLE))); 57 | algorithms.add(new QuicksortImproved(threshold, new QuicksortVariant2(PivotStrategy.MEDIAN3))); 58 | 59 | // Variant 3 60 | algorithms.add(new QuicksortImproved(threshold, new QuicksortVariant3(PivotStrategy.MIDDLE))); 61 | algorithms.add(new QuicksortImproved(threshold, new QuicksortVariant3(PivotStrategy.MEDIAN3))); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/sort/method/radixsort/RadixSortWithDynamicLists.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.radixsort; 2 | 3 | import static eu.happycoders.sort.method.radixsort.RadixSortHelper.calculateDivisor; 4 | import static eu.happycoders.sort.method.radixsort.RadixSortHelper.checkIfContainsNegatives; 5 | import static eu.happycoders.sort.method.radixsort.RadixSortHelper.getNumberOfDigits; 6 | import static eu.happycoders.sort.utils.ArrayUtils.getMaximum; 7 | 8 | import eu.happycoders.sort.method.Counters; 9 | import eu.happycoders.sort.method.SortAlgorithm; 10 | import eu.happycoders.sort.utils.NotImplementedException; 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | /** 15 | * Radix Sort implementation using dynamic lists as buckets. 16 | * 17 | * @author Sven Woltmann 18 | */ 19 | public class RadixSortWithDynamicLists implements SortAlgorithm { 20 | 21 | @Override 22 | public void sort(int[] elements) { 23 | checkIfContainsNegatives(elements); 24 | int max = getMaximum(elements); 25 | int numberOfDigits = getNumberOfDigits(max); 26 | 27 | for (int digitIndex = 0; digitIndex < numberOfDigits; digitIndex++) { 28 | sortByDigit(elements, digitIndex); 29 | } 30 | } 31 | 32 | private void sortByDigit(int[] elements, int digitIndex) { 33 | Bucket[] buckets = partition(elements, digitIndex); 34 | collect(buckets, elements); 35 | } 36 | 37 | private Bucket[] partition(int[] elements, int digitIndex) { 38 | Bucket[] buckets = createBuckets(); 39 | distributeToBuckets(elements, digitIndex, buckets); 40 | return buckets; 41 | } 42 | 43 | private Bucket[] createBuckets() { 44 | Bucket[] buckets = new Bucket[10]; 45 | for (int i = 0; i < 10; i++) { 46 | buckets[i] = new Bucket(); 47 | } 48 | return buckets; 49 | } 50 | 51 | private void distributeToBuckets(int[] elements, int digitIndex, Bucket[] buckets) { 52 | int divisor = calculateDivisor(digitIndex); 53 | 54 | for (int element : elements) { 55 | int digit = element / divisor % 10; 56 | buckets[digit].add(element); 57 | } 58 | } 59 | 60 | private void collect(Bucket[] buckets, int[] elements) { 61 | int targetIndex = 0; 62 | for (Bucket bucket : buckets) { 63 | for (int element : bucket.getElements()) { 64 | elements[targetIndex] = element; 65 | targetIndex++; 66 | } 67 | } 68 | } 69 | 70 | @Override 71 | public void sortWithCounters(int[] elements, Counters counters) { 72 | throw new NotImplementedException(); 73 | } 74 | 75 | @Override 76 | public boolean supportsCounting() { 77 | return false; 78 | } 79 | 80 | private static class Bucket { 81 | private final List elements = new ArrayList<>(); 82 | 83 | private void add(int element) { 84 | elements.add(element); 85 | } 86 | 87 | private List getElements() { 88 | return elements; 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/quicksort/DualPivotQuicksortPartitionTest.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.quicksort; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertTrue; 4 | 5 | import eu.happycoders.sort.method.quicksort.DualPivotQuicksort.PivotStrategy; 6 | import eu.happycoders.sort.utils.ArrayUtils; 7 | import java.util.concurrent.ThreadLocalRandom; 8 | import java.util.function.Function; 9 | import org.junit.jupiter.api.*; 10 | 11 | class DualPivotQuicksortPartitionTest { 12 | 13 | // ---- PivotStrategy.LEFT_RIGHT ---- 14 | 15 | @RepeatedTest(100) 16 | void partitionLEFT_RIGHT_randomArray_partitioned() { 17 | testPartitioning(PivotStrategy.LEFT_RIGHT, ArrayUtils::createRandomArray); 18 | } 19 | 20 | @Test 21 | void partitionLEFT_RIGHT_sortedArray_partitioned() { 22 | testPartitioning(PivotStrategy.LEFT_RIGHT, ArrayUtils::createSortedArray); 23 | } 24 | 25 | @Test 26 | void partitionLEFT_RIGHT_reversedArray_partitioned() { 27 | testPartitioning(PivotStrategy.LEFT_RIGHT, ArrayUtils::createReversedArray); 28 | } 29 | 30 | // ---- PivotStrategy.MIDDLES ---- 31 | 32 | @RepeatedTest(100) 33 | void partitionMIDDLES_randomArray_partitioned() { 34 | testPartitioning(PivotStrategy.THIRDS, ArrayUtils::createRandomArray); 35 | } 36 | 37 | @Test 38 | void partitionMIDDLES_sortedArray_partitioned() { 39 | testPartitioning(PivotStrategy.THIRDS, ArrayUtils::createSortedArray); 40 | } 41 | 42 | @Test 43 | void partitionMIDDLES_reversedArray_partitioned() { 44 | testPartitioning(PivotStrategy.THIRDS, ArrayUtils::createReversedArray); 45 | } 46 | 47 | // ---- private methods ---- 48 | 49 | private void testPartitioning( 50 | PivotStrategy pivotStrategy, Function arraySupplier) { 51 | int[] elements = arraySupplier.apply(randomSize()); 52 | int[] pivotPos = 53 | new DualPivotQuicksort(pivotStrategy).partition(elements, 0, elements.length - 1); 54 | int pivotPos0 = pivotPos[0]; 55 | int pivotPos1 = pivotPos[1]; 56 | 57 | assertTrue(pivotPos0 >= 0); 58 | assertTrue(pivotPos0 < pivotPos1); 59 | assertTrue(pivotPos1 < elements.length); 60 | assertTrue(elements[pivotPos0] <= elements[pivotPos1]); 61 | 62 | // All elements before pivotPos0 must be <= elements[pivotPos0] 63 | for (int i = 0; i < pivotPos0; i++) { 64 | assertTrue(elements[i] <= elements[pivotPos0]); 65 | } 66 | // All elements between pivotPos0 and pivotPos1 67 | // must be >= elements[pivotPos0] and <= elements[pivotPos1] 68 | for (int i = pivotPos0 + 1; i < pivotPos1; i++) { 69 | assertTrue(elements[i] >= elements[pivotPos0]); 70 | assertTrue(elements[i] <= elements[pivotPos1]); 71 | } 72 | // All elements after pivotPos1 must be >= elements[pivotPos1] 73 | for (int i = pivotPos1 + 1; i < elements.length; i++) { 74 | assertTrue(elements[i] >= elements[pivotPos1]); 75 | } 76 | } 77 | 78 | private int randomSize() { 79 | return ThreadLocalRandom.current().nextInt(2, 10_000); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/sort/method/quicksort/PivotHelper.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.quicksort; 2 | 3 | import eu.happycoders.sort.utils.ArrayUtils; 4 | import java.util.concurrent.ThreadLocalRandom; 5 | 6 | /** 7 | * Helper methods for calculating the pivot position. 8 | * 9 | * @author Sven Woltmann 10 | */ 11 | public final class PivotHelper { 12 | 13 | private PivotHelper() {} 14 | 15 | static void findPivotAndMoveRight( 16 | int[] elements, int left, int right, PivotStrategy pivotStrategy) { 17 | int pivotPos = findPivotPos(elements, left, right, pivotStrategy); 18 | if (pivotPos != right) { 19 | ArrayUtils.swap(elements, pivotPos, right); 20 | } 21 | } 22 | 23 | static int findPivotPos(int[] elements, int left, int right, PivotStrategy pivotStrategy) { 24 | return switch (pivotStrategy) { 25 | case RIGHT -> right; 26 | case RANDOM -> ThreadLocalRandom.current().nextInt(left, right + 1); 27 | case LEFT -> left; 28 | case MIDDLE -> { 29 | // optimization: if length is 2, take right element, saves one swap 30 | int rl = right - left; 31 | yield rl < 2 ? right : left + rl / 2; 32 | } 33 | case MEDIAN3 -> getMedian3Pos(elements, left, right); 34 | }; 35 | } 36 | 37 | private static int getMedian3Pos(int[] elements, int left, int right) { 38 | int rl = right - left; 39 | if (rl < 2) { 40 | return right; 41 | } 42 | 43 | // We must split the array into 4 parts and take the elements at the 44 | // borders. If we took the left or right element, we'll end up with O(n²) 45 | // for descending input data when swapping the pivot element with the 46 | // rightmost element. 47 | 48 | // Reason: First, the middle element will be chosen as Pivot element and 49 | // moved to the right, therefore the smallest element will be in the middle. 50 | 51 | // Example: 100 99 98 ... 53 52 1 50 49 ... 4 3 2 51 52 | 53 | // After swapping large elements to the right and small elements to the 54 | // left, the second smallest element will be at the left. 55 | 56 | // Example: 2 3 4 ... 49 50 1 52 53 ... 98 99 100 51 57 | 58 | // In the next iteration, the middle will be the last element of the left 59 | // sub-array. Therefore the two smallest elements of the array will be at 60 | // its borders: 61 | 62 | // Example: 2 3 4 ... 49 50 1 63 | 64 | // The median will now be the left element, which will not split the left 65 | // array into two equal-sized areas, but one of size 1 and one of size 66 | // n-2. Therefore, we'll end up with quadratic time instead of 67 | // quasi-linear time. 68 | 69 | int middle = left + rl / 2; 70 | int first = left + (middle - left) / 2; 71 | int last = right - (right - middle) / 2; 72 | 73 | int l = elements[first]; 74 | int m = elements[middle]; 75 | int r = elements[last]; 76 | 77 | if (l < r) { 78 | if (l >= m) { 79 | return first; 80 | } else if (r < m) { 81 | return last; 82 | } 83 | } else { 84 | if (l < m) { 85 | return first; 86 | } else if (r >= m) { 87 | return last; 88 | } 89 | } 90 | return middle; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/sort/method/radixsort/RadixSortWithCountingSortAndCustomBase.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.radixsort; 2 | 3 | import static eu.happycoders.sort.method.radixsort.RadixSortHelper.calculateDivisor; 4 | import static eu.happycoders.sort.method.radixsort.RadixSortHelper.checkIfContainsNegatives; 5 | import static eu.happycoders.sort.method.radixsort.RadixSortHelper.getNumberOfDigits; 6 | import static eu.happycoders.sort.utils.ArrayUtils.getMaximum; 7 | 8 | import eu.happycoders.sort.method.Counters; 9 | import eu.happycoders.sort.method.SortAlgorithm; 10 | import eu.happycoders.sort.utils.NotImplementedException; 11 | 12 | /** 13 | * Radix Sort implementation using counting sort and with a customizable base. 14 | * 15 | * @author Sven Woltmann 16 | */ 17 | public class RadixSortWithCountingSortAndCustomBase implements SortAlgorithm { 18 | 19 | private final int base; 20 | 21 | public RadixSortWithCountingSortAndCustomBase(int base) { 22 | this.base = base; 23 | } 24 | 25 | @Override 26 | public void sort(int[] elements) { 27 | checkIfContainsNegatives(elements); 28 | int max = getMaximum(elements); 29 | int numberOfDigits = getNumberOfDigits(max, base); 30 | 31 | // Remember input array 32 | int[] inputArray = elements; 33 | 34 | for (int digitIndex = 0; digitIndex < numberOfDigits; digitIndex++) { 35 | elements = sortByDigit(elements, digitIndex); 36 | } 37 | 38 | // Copy sorted elements back to input array 39 | System.arraycopy(elements, 0, inputArray, 0, elements.length); 40 | } 41 | 42 | private int[] sortByDigit(int[] elements, int digitIndex) { 43 | int[] counts = countDigits(elements, digitIndex); 44 | int[] prefixSums = calculatePrefixSums(counts); 45 | return collectElements(elements, digitIndex, prefixSums); 46 | } 47 | 48 | private int[] countDigits(int[] elements, int digitIndex) { 49 | int[] counts = new int[base]; 50 | int divisor = calculateDivisor(digitIndex, base); 51 | for (int element : elements) { 52 | int digit = element / divisor % base; 53 | counts[digit]++; 54 | } 55 | return counts; 56 | } 57 | 58 | private int[] calculatePrefixSums(int[] counts) { 59 | int[] prefixSums = new int[base]; 60 | prefixSums[0] = counts[0]; 61 | for (int i = 1; i < base; i++) { 62 | prefixSums[i] = prefixSums[i - 1] + counts[i]; 63 | } 64 | return prefixSums; 65 | } 66 | 67 | private int[] collectElements(int[] elements, int digitIndex, int[] prefixSums) { 68 | int divisor = calculateDivisor(digitIndex, base); 69 | int[] target = new int[elements.length]; 70 | for (int i = elements.length - 1; i >= 0; i--) { 71 | int element = elements[i]; 72 | int digit = element / divisor % base; 73 | target[--prefixSums[digit]] = element; 74 | } 75 | return target; 76 | } 77 | 78 | @Override 79 | public void sortWithCounters(int[] elements, Counters counters) { 80 | throw new NotImplementedException(); 81 | } 82 | 83 | @Override 84 | public String getName() { 85 | return this.getClass().getSimpleName() + "(" + base + ")"; 86 | } 87 | 88 | @Override 89 | public boolean supportsCounting() { 90 | return false; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /results/2020-05-30/Test_Results_Bubble_Sort.txt: -------------------------------------------------------------------------------- 1 | --- Results for iteration 50 for: BubbleSort (order: random) --- 2 | BubbleSort/random/1024 -> fastest: 0.788 ms, median: 0.947 ms 3 | BubbleSort/random/2048 -> fastest: 2.841 ms, median: 3.604 ms 4 | BubbleSort/random/4096 -> fastest: 12.256 ms, median: 13.132 ms 5 | BubbleSort/random/8192 -> fastest: 59.631 ms, median: 61.731 ms 6 | BubbleSort/random/16384 -> fastest: 290.551 ms, median: 294.641 ms 7 | BubbleSort/random/32768 -> fastest: 1,258.793 ms, median: 1,272.065 ms 8 | BubbleSort/random/65536 -> fastest: 5,155.777 ms, median: 5,196.817 ms 9 | BubbleSort/random/131072 -> fastest: 20,798.152 ms, median: 20,903.544 ms 10 | 11 | --- Results for iteration 50 for: BubbleSort (order: ascending) --- 12 | BubbleSort/ascending/1024 -> fastest: 0.001 ms, median: 0.001 ms 13 | BubbleSort/ascending/2048 -> fastest: 0.001 ms, median: 0.001 ms 14 | BubbleSort/ascending/4096 -> fastest: 0.002 ms, median: 0.002 ms 15 | BubbleSort/ascending/8192 -> fastest: 0.003 ms, median: 0.004 ms 16 | BubbleSort/ascending/16384 -> fastest: 0.007 ms, median: 0.008 ms 17 | BubbleSort/ascending/32768 -> fastest: 0.011 ms, median: 0.015 ms 18 | BubbleSort/ascending/65536 -> fastest: 0.023 ms, median: 0.030 ms 19 | BubbleSort/ascending/131072 -> fastest: 0.045 ms, median: 0.060 ms 20 | BubbleSort/ascending/262144 -> fastest: 0.093 ms, median: 0.129 ms 21 | BubbleSort/ascending/524288 -> fastest: 0.187 ms, median: 0.262 ms 22 | BubbleSort/ascending/1048576 -> fastest: 0.369 ms, median: 0.530 ms 23 | BubbleSort/ascending/2097152 -> fastest: 0.753 ms, median: 1.078 ms 24 | BubbleSort/ascending/4194304 -> fastest: 1.457 ms, median: 2.171 ms 25 | BubbleSort/ascending/8388608 -> fastest: 2.927 ms, median: 4.307 ms 26 | BubbleSort/ascending/16777216 -> fastest: 5.943 ms, median: 7.404 ms 27 | BubbleSort/ascending/33554432 -> fastest: 11.731 ms, median: 16.478 ms 28 | BubbleSort/ascending/67108864 -> fastest: 23.117 ms, median: 24.765 ms 29 | BubbleSort/ascending/134217728 -> fastest: 47.000 ms, median: 51.074 ms 30 | BubbleSort/ascending/268435456 -> fastest: 94.369 ms, median: 98.831 ms 31 | BubbleSort/ascending/536870912 -> fastest: 187.986 ms, median: 192.509 ms 32 | 33 | --- Results for iteration 50 for: BubbleSort (order: descending) --- 34 | BubbleSort/descending/1024 -> fastest: 0.532 ms, median: 0.536 ms 35 | BubbleSort/descending/2048 -> fastest: 2.157 ms, median: 2.162 ms 36 | BubbleSort/descending/4096 -> fastest: 8.675 ms, median: 8.784 ms 37 | BubbleSort/descending/8192 -> fastest: 34.208 ms, median: 35.182 ms 38 | BubbleSort/descending/16384 -> fastest: 139.142 ms, median: 141.161 ms 39 | BubbleSort/descending/32768 -> fastest: 563.101 ms, median: 566.388 ms 40 | BubbleSort/descending/65536 -> fastest: 2,247.305 ms, median: 2,267.846 ms 41 | BubbleSort/descending/131072 -> fastest: 9,035.317 ms, median: 9,068.245 ms 42 | BubbleSort/descending/262144 -> fastest: 36,153.946 ms, median: 36,286.469 ms 43 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/sort/method/quicksort/QuicksortImproved.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.quicksort; 2 | 3 | import eu.happycoders.sort.method.Counters; 4 | import eu.happycoders.sort.method.InsertionSort; 5 | import eu.happycoders.sort.method.PartitioningAlgorithm; 6 | import eu.happycoders.sort.method.SortAlgorithm; 7 | 8 | /** 9 | * Quicksort combined with Insertion Sort for small arrays. 10 | * 11 | * @author Sven Woltmann 12 | */ 13 | public class QuicksortImproved implements SortAlgorithm { 14 | 15 | private final int threshold; 16 | private final PartitioningAlgorithm partitioningAlgorithm; 17 | private final InsertionSort insertionSort; 18 | 19 | /** 20 | * Constructs the Quicksort instance. 21 | * 22 | * @param threshold when the array to be sorted is not longer than this threshold, the algorithm 23 | * switches to Insertion Sort 24 | * @param partitioningAlgorithm the quicksort algorithm to use 25 | */ 26 | public QuicksortImproved(int threshold, PartitioningAlgorithm partitioningAlgorithm) { 27 | this.threshold = threshold; 28 | this.partitioningAlgorithm = partitioningAlgorithm; 29 | this.insertionSort = new InsertionSort(); 30 | } 31 | 32 | @Override 33 | public String getName() { 34 | return this.getClass().getSimpleName() 35 | + "(threshold: " 36 | + threshold 37 | + ", partitioning: " 38 | + partitioningAlgorithm.getName() 39 | + ")"; 40 | } 41 | 42 | @Override 43 | public void sort(int[] elements) { 44 | quicksort(elements, 0, elements.length - 1); 45 | } 46 | 47 | private void quicksort(int[] elements, int left, int right) { 48 | // End of recursion reached? 49 | if (left >= right) { 50 | return; 51 | } 52 | 53 | // Threshold for insertion sort reached? 54 | if (right - left < threshold) { 55 | insertionSort.sort(elements, left, right + 1); 56 | return; 57 | } 58 | 59 | int pivotPos = partitioningAlgorithm.partition(elements, left, right); 60 | quicksort(elements, left, pivotPos - 1); 61 | quicksort(elements, pivotPos + 1, right); 62 | } 63 | 64 | @Override 65 | public void sortWithCounters(int[] elements, Counters counters) { 66 | quicksortWithCounters(elements, 0, elements.length - 1, counters); 67 | } 68 | 69 | private void quicksortWithCounters(int[] elements, int left, int right, Counters counters) { 70 | // Nothing to sort? 71 | if (left == right) { 72 | return; 73 | } 74 | 75 | // Threshold for insertion sort reached? 76 | if (right - left < threshold) { 77 | insertionSort.sortWithCounters(elements, left, right + 1, counters); 78 | return; 79 | } 80 | 81 | int pivotPos = partitioningAlgorithm.partitionWithCounters(elements, left, right, counters); 82 | quicksortWithCounters(elements, left, pivotPos - 1, counters); 83 | quicksortWithCounters(elements, pivotPos + 1, right, counters); 84 | } 85 | 86 | @Override 87 | public boolean isSuitableForInputSize(int size) { 88 | return partitioningAlgorithm.isSuitableForInputSize(size); 89 | } 90 | 91 | @Override 92 | public boolean isSuitableForSortedInput(int size) { 93 | return partitioningAlgorithm.isSuitableForSortedInput(size); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/sort/method/radixsort/RadixSortWithDynamicListsAndCustomBase.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.radixsort; 2 | 3 | import static eu.happycoders.sort.method.radixsort.RadixSortHelper.calculateDivisor; 4 | import static eu.happycoders.sort.method.radixsort.RadixSortHelper.checkIfContainsNegatives; 5 | import static eu.happycoders.sort.method.radixsort.RadixSortHelper.getNumberOfDigits; 6 | import static eu.happycoders.sort.utils.ArrayUtils.getMaximum; 7 | 8 | import eu.happycoders.sort.method.Counters; 9 | import eu.happycoders.sort.method.SortAlgorithm; 10 | import eu.happycoders.sort.utils.NotImplementedException; 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | /** 15 | * Radix Sort implementation using dynamic lists as buckets and with a customizable base. 16 | * 17 | * @author Sven Woltmann 18 | */ 19 | public class RadixSortWithDynamicListsAndCustomBase implements SortAlgorithm { 20 | 21 | private final int base; 22 | 23 | public RadixSortWithDynamicListsAndCustomBase(int base) { 24 | this.base = base; 25 | } 26 | 27 | @Override 28 | public void sort(int[] elements) { 29 | checkIfContainsNegatives(elements); 30 | int max = getMaximum(elements); 31 | int numberOfDigits = getNumberOfDigits(max, base); 32 | 33 | for (int digitIndex = 0; digitIndex < numberOfDigits; digitIndex++) { 34 | sortByDigit(elements, digitIndex); 35 | } 36 | } 37 | 38 | private void sortByDigit(int[] elements, int digitIndex) { 39 | Bucket[] buckets = partition(elements, digitIndex); 40 | collect(buckets, elements); 41 | } 42 | 43 | private Bucket[] partition(int[] elements, int digitIndex) { 44 | Bucket[] buckets = createBuckets(); 45 | distributeToBuckets(elements, digitIndex, buckets); 46 | return buckets; 47 | } 48 | 49 | private Bucket[] createBuckets() { 50 | Bucket[] buckets = new Bucket[base]; 51 | for (int i = 0; i < base; i++) { 52 | buckets[i] = new Bucket(); 53 | } 54 | return buckets; 55 | } 56 | 57 | private void distributeToBuckets(int[] elements, int digitIndex, Bucket[] buckets) { 58 | int divisor = calculateDivisor(digitIndex, base); 59 | 60 | for (int element : elements) { 61 | int digit = element / divisor % base; 62 | buckets[digit].add(element); 63 | } 64 | } 65 | 66 | private void collect(Bucket[] buckets, int[] elements) { 67 | int targetIndex = 0; 68 | for (Bucket bucket : buckets) { 69 | for (int element : bucket.getElements()) { 70 | elements[targetIndex] = element; 71 | targetIndex++; 72 | } 73 | } 74 | } 75 | 76 | @Override 77 | public void sortWithCounters(int[] elements, Counters counters) { 78 | throw new NotImplementedException(); 79 | } 80 | 81 | @Override 82 | public String getName() { 83 | return this.getClass().getSimpleName() + "(" + base + ")"; 84 | } 85 | 86 | @Override 87 | public boolean supportsCounting() { 88 | return false; 89 | } 90 | 91 | private static class Bucket { 92 | private final List elements = new ArrayList<>(); 93 | 94 | private void add(int element) { 95 | elements.add(element); 96 | } 97 | 98 | private List getElements() { 99 | return elements; 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/sort/method/countingsort/CountingSort.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.countingsort; 2 | 3 | import eu.happycoders.sort.method.Counters; 4 | import eu.happycoders.sort.method.SortAlgorithm; 5 | 6 | /** 7 | * Counting Sort implementation for performance tests. 8 | * 9 | *

For simplicity, this implementation allows only elements >= 0. 10 | * 11 | * @author Sven Woltmann 12 | */ 13 | public class CountingSort implements SortAlgorithm { 14 | 15 | private static final int MAX_VALUE_TO_SORT = Integer.MAX_VALUE / 2; 16 | private static final int MIN_VALUE_TO_SORT = Integer.MIN_VALUE / 2; 17 | 18 | @Override 19 | public void sort(int[] elements) { 20 | Boundaries boundaries = findBoundaries(elements); 21 | int[] counts = new int[boundaries.max - boundaries.min + 1]; 22 | 23 | // Phase 1: Count 24 | for (int element : elements) { 25 | counts[element - boundaries.min]++; 26 | } 27 | 28 | // Phase 2: Write results back 29 | int targetPos = 0; 30 | for (int i = 0; i < counts.length; i++) { 31 | for (int j = 0; j < counts[i]; j++) { 32 | elements[targetPos++] = i + boundaries.min; 33 | } 34 | } 35 | } 36 | 37 | private Boundaries findBoundaries(int[] elements) { 38 | int min = Integer.MAX_VALUE; 39 | int max = Integer.MIN_VALUE; 40 | for (int element : elements) { 41 | if (element > MAX_VALUE_TO_SORT) { 42 | throw new IllegalArgumentException( 43 | "Element " + element + " is greater than maximum " + MAX_VALUE_TO_SORT); 44 | } 45 | if (element < MIN_VALUE_TO_SORT) { 46 | throw new IllegalArgumentException( 47 | "Element " + element + " is less than minimum " + MIN_VALUE_TO_SORT); 48 | } 49 | if (element > max) { 50 | max = element; 51 | } 52 | if (element < min) { 53 | min = element; 54 | } 55 | } 56 | return new Boundaries(min, max); 57 | } 58 | 59 | private static class Boundaries { 60 | private final int min; 61 | private final int max; 62 | 63 | public Boundaries(int min, int max) { 64 | this.min = min; 65 | this.max = max; 66 | } 67 | } 68 | 69 | @Override 70 | public void sortWithCounters(int[] elements, Counters counters) { 71 | Boundaries boundaries = findBoundaries(elements); 72 | 73 | int length = elements.length; 74 | counters.addReads(length); 75 | counters.addIterations(length); 76 | counters.addComparisons(length); 77 | 78 | int[] counts = new int[boundaries.max - boundaries.min + 1]; 79 | 80 | // Phase 1: Count 81 | counters.addIterations(length); 82 | counters.addReads(length); // read elements[i] 83 | counters.addReadsAndWrites(length); // inc counts[...] 84 | for (int i = 0; i < length; i++) { 85 | counts[elements[i] - boundaries.min]++; 86 | } 87 | 88 | // Phase 2: Write results back 89 | int targetPos = 0; 90 | counters.addIterations(counts.length); // outer 91 | counters.addIterations(length); // all inner combined 92 | counters.addReads(counts.length); // read counts[i] 93 | counters.addWrites(length); // write elements[targetPos++] 94 | for (int i = 0; i < counts.length; i++) { 95 | for (int j = 0; j < counts[i]; j++) { 96 | elements[targetPos++] = i + boundaries.min; 97 | } 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/sort/method/radixsort/RadixSortWithArrays.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.radixsort; 2 | 3 | import static eu.happycoders.sort.method.radixsort.RadixSortHelper.calculateDivisor; 4 | import static eu.happycoders.sort.method.radixsort.RadixSortHelper.checkIfContainsNegatives; 5 | import static eu.happycoders.sort.method.radixsort.RadixSortHelper.getNumberOfDigits; 6 | import static eu.happycoders.sort.utils.ArrayUtils.getMaximum; 7 | 8 | import eu.happycoders.sort.method.Counters; 9 | import eu.happycoders.sort.method.SortAlgorithm; 10 | import eu.happycoders.sort.utils.NotImplementedException; 11 | 12 | /** 13 | * Radix Sort implementation using arrays as buckets. 14 | * 15 | * @author Sven Woltmann 16 | */ 17 | public class RadixSortWithArrays implements SortAlgorithm { 18 | 19 | @Override 20 | public void sort(int[] elements) { 21 | checkIfContainsNegatives(elements); 22 | int max = getMaximum(elements); 23 | int numberOfDigits = getNumberOfDigits(max); 24 | 25 | for (int digitIndex = 0; digitIndex < numberOfDigits; digitIndex++) { 26 | sortByDigit(elements, digitIndex); 27 | } 28 | } 29 | 30 | private void sortByDigit(int[] elements, int digitIndex) { 31 | Bucket[] buckets = partition(elements, digitIndex); 32 | collect(buckets, elements); 33 | } 34 | 35 | private Bucket[] partition(int[] elements, int digitIndex) { 36 | int[] counts = countDigits(elements, digitIndex); 37 | Bucket[] buckets = createBuckets(counts); 38 | distributeToBuckets(elements, digitIndex, buckets); 39 | return buckets; 40 | } 41 | 42 | private int[] countDigits(int[] elements, int digitIndex) { 43 | int[] counts = new int[10]; 44 | int divisor = calculateDivisor(digitIndex); 45 | for (int element : elements) { 46 | int digit = element / divisor % 10; 47 | counts[digit]++; 48 | } 49 | return counts; 50 | } 51 | 52 | private Bucket[] createBuckets(int[] counts) { 53 | Bucket[] buckets = new Bucket[10]; 54 | for (int i = 0; i < 10; i++) { 55 | buckets[i] = new Bucket(counts[i]); 56 | } 57 | return buckets; 58 | } 59 | 60 | private void distributeToBuckets(int[] elements, int digitIndex, Bucket[] buckets) { 61 | int divisor = calculateDivisor(digitIndex); 62 | 63 | for (int element : elements) { 64 | int digit = element / divisor % 10; 65 | buckets[digit].add(element); 66 | } 67 | } 68 | 69 | private void collect(Bucket[] buckets, int[] elements) { 70 | int targetIndex = 0; 71 | for (Bucket bucket : buckets) { 72 | for (int element : bucket.getElements()) { 73 | elements[targetIndex] = element; 74 | targetIndex++; 75 | } 76 | } 77 | } 78 | 79 | @Override 80 | public void sortWithCounters(int[] elements, Counters counters) { 81 | throw new NotImplementedException(); 82 | } 83 | 84 | @Override 85 | public boolean supportsCounting() { 86 | return false; 87 | } 88 | 89 | private static class Bucket { 90 | private final int[] elements; 91 | private int addIndex; 92 | 93 | private Bucket(int size) { 94 | elements = new int[size]; 95 | } 96 | 97 | private void add(int element) { 98 | elements[addIndex] = element; 99 | addIndex++; 100 | } 101 | 102 | private int[] getElements() { 103 | return elements; 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /results/2020-05-30/Test_Results_Insertion_Sort.txt: -------------------------------------------------------------------------------- 1 | --- Results for iteration 50 for: InsertionSort (order: random) --- 2 | InsertionSort/random/1024 -> fastest: 0.088 ms, median: 0.095 ms 3 | InsertionSort/random/2048 -> fastest: 0.339 ms, median: 0.355 ms 4 | InsertionSort/random/4096 -> fastest: 1.323 ms, median: 1.378 ms 5 | InsertionSort/random/8192 -> fastest: 5.292 ms, median: 5.494 ms 6 | InsertionSort/random/16384 -> fastest: 21.349 ms, median: 21.890 ms 7 | InsertionSort/random/32768 -> fastest: 85.546 ms, median: 87.859 ms 8 | InsertionSort/random/65536 -> fastest: 343.144 ms, median: 350.428 ms 9 | InsertionSort/random/131072 -> fastest: 1,378.011 ms, median: 1,398.919 ms 10 | InsertionSort/random/262144 -> fastest: 5,636.412 ms, median: 5,706.817 ms 11 | InsertionSort/random/524288 -> fastest: 22,749.327 ms, median: 23,009.682 ms 12 | 13 | --- Results for iteration 50 for: InsertionSort (order: ascending) --- 14 | InsertionSort/ascending/1024 -> fastest: 0.002 ms, median: 0.002 ms 15 | InsertionSort/ascending/2048 -> fastest: 0.003 ms, median: 0.003 ms 16 | InsertionSort/ascending/4096 -> fastest: 0.005 ms, median: 0.006 ms 17 | InsertionSort/ascending/8192 -> fastest: 0.011 ms, median: 0.011 ms 18 | InsertionSort/ascending/16384 -> fastest: 0.021 ms, median: 0.021 ms 19 | InsertionSort/ascending/32768 -> fastest: 0.041 ms, median: 0.042 ms 20 | InsertionSort/ascending/65536 -> fastest: 0.082 ms, median: 0.084 ms 21 | InsertionSort/ascending/131072 -> fastest: 0.163 ms, median: 0.168 ms 22 | InsertionSort/ascending/262144 -> fastest: 0.331 ms, median: 0.351 ms 23 | InsertionSort/ascending/524288 -> fastest: 0.670 ms, median: 0.710 ms 24 | InsertionSort/ascending/1048576 -> fastest: 1.334 ms, median: 1.419 ms 25 | InsertionSort/ascending/2097152 -> fastest: 2.667 ms, median: 2.811 ms 26 | InsertionSort/ascending/4194304 -> fastest: 5.343 ms, median: 5.621 ms 27 | InsertionSort/ascending/8388608 -> fastest: 10.745 ms, median: 11.190 ms 28 | InsertionSort/ascending/16777216 -> fastest: 21.748 ms, median: 22.290 ms 29 | InsertionSort/ascending/33554432 -> fastest: 43.500 ms, median: 44.455 ms 30 | InsertionSort/ascending/67108864 -> fastest: 86.143 ms, median: 87.079 ms 31 | InsertionSort/ascending/134217728 -> fastest: 172.431 ms, median: 173.980 ms 32 | InsertionSort/ascending/268435456 -> fastest: 343.419 ms, median: 346.236 ms 33 | InsertionSort/ascending/536870912 -> fastest: 688.349 ms, median: 693.310 ms 34 | 35 | --- Results for iteration 50 for: InsertionSort (order: descending) --- 36 | InsertionSort/descending/1024 -> fastest: 0.177 ms, median: 0.178 ms 37 | InsertionSort/descending/2048 -> fastest: 0.679 ms, median: 0.690 ms 38 | InsertionSort/descending/4096 -> fastest: 2.676 ms, median: 2.704 ms 39 | InsertionSort/descending/8192 -> fastest: 10.629 ms, median: 10.858 ms 40 | InsertionSort/descending/16384 -> fastest: 42.709 ms, median: 43.609 ms 41 | InsertionSort/descending/32768 -> fastest: 171.607 ms, median: 175.800 ms 42 | InsertionSort/descending/65536 -> fastest: 684.408 ms, median: 697.590 ms 43 | InsertionSort/descending/131072 -> fastest: 2,807.428 ms, median: 2,839.991 ms 44 | InsertionSort/descending/262144 -> fastest: 11,379.433 ms, median: 11,517.355 ms 45 | InsertionSort/descending/524288 -> fastest: 45,939.779 ms, median: 46,309.272 ms 46 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/sort/method/mergesort/NaturalMergeSort.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.mergesort; 2 | 3 | import eu.happycoders.sort.method.Counters; 4 | import eu.happycoders.sort.method.SortAlgorithm; 5 | import eu.happycoders.sort.utils.NotImplementedException; 6 | 7 | /** 8 | * Natural merge sort implementation for performance tests. 9 | * 10 | * @author Sven Woltmann 11 | */ 12 | public class NaturalMergeSort implements SortAlgorithm { 13 | 14 | @Override 15 | public void sort(int[] elements) { 16 | int numElements = elements.length; 17 | 18 | int[] tmp = new int[numElements]; 19 | int[] starts = new int[numElements + 1]; 20 | 21 | // Step 1: identify runs 22 | int runCount = 0; 23 | starts[0] = 0; 24 | for (int i = 1; i <= numElements; i++) { 25 | if (i == numElements || elements[i] < elements[i - 1]) { 26 | starts[++runCount] = i; 27 | } 28 | } 29 | 30 | // Step 2: merge runs, until only 1 run is left 31 | int[] from = elements; 32 | int[] to = tmp; 33 | 34 | while (runCount > 1) { 35 | int newRunCount = 0; 36 | 37 | // Merge two runs each 38 | for (int i = 0; i < runCount - 1; i += 2) { 39 | merge(from, to, starts[i], starts[i + 1], starts[i + 2]); 40 | starts[newRunCount++] = starts[i]; 41 | } 42 | 43 | // Odd number of runs? Copy the last one 44 | if (isOdd(runCount)) { 45 | int lastStart = starts[runCount - 1]; 46 | System.arraycopy(from, lastStart, to, lastStart, numElements - lastStart); 47 | starts[newRunCount++] = lastStart; 48 | } 49 | 50 | // Prepare for next round... 51 | starts[newRunCount] = numElements; 52 | runCount = newRunCount; 53 | 54 | // Swap "from" and "to" arrays 55 | int[] help = from; 56 | from = to; 57 | to = help; 58 | } 59 | 60 | // If final run is not in "elements", copy it there 61 | if (isNotSameArray(from, elements)) { 62 | System.arraycopy(from, 0, elements, 0, numElements); 63 | } 64 | } 65 | 66 | private void merge(int[] source, int[] target, int startLeft, int startRight, int endRight) { 67 | int leftPos = startLeft; 68 | int rightPos = startRight; 69 | int targetPos = startLeft; 70 | 71 | // As long as both arrays contain elements... 72 | while (leftPos < startRight && rightPos < endRight) { 73 | // Which one is smaller? 74 | int leftValue = source[leftPos]; 75 | int rightValue = source[rightPos]; 76 | if (leftValue <= rightValue) { 77 | target[targetPos++] = leftValue; 78 | leftPos++; 79 | } else { 80 | target[targetPos++] = rightValue; 81 | rightPos++; 82 | } 83 | } 84 | // Copy the rest 85 | while (leftPos < startRight) { 86 | target[targetPos++] = source[leftPos++]; 87 | } 88 | while (rightPos < endRight) { 89 | target[targetPos++] = source[rightPos++]; 90 | } 91 | } 92 | 93 | private boolean isOdd(int number) { 94 | return number % 2 != 0; 95 | } 96 | 97 | @SuppressWarnings("PMD.CompareObjectsWithEquals") // We want to know if it's the same instance! 98 | private boolean isNotSameArray(int[] array1, int[] array2) { 99 | return array1 != array2; 100 | } 101 | 102 | @Override 103 | public void sortWithCounters(int[] elements, Counters counters) { 104 | throw new NotImplementedException(); 105 | } 106 | 107 | @Override 108 | public boolean supportsCounting() { 109 | return false; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/sort/method/quicksort/DualPivotQuicksortImproved.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.quicksort; 2 | 3 | import eu.happycoders.sort.method.Counters; 4 | import eu.happycoders.sort.method.InsertionSort; 5 | import eu.happycoders.sort.method.SortAlgorithm; 6 | import eu.happycoders.sort.method.quicksort.DualPivotQuicksort.PivotStrategy; 7 | 8 | /** 9 | * Dual-pivot Quicksort implementation for performance tests. 10 | * 11 | * @author Sven Woltmann 12 | */ 13 | public class DualPivotQuicksortImproved implements SortAlgorithm { 14 | 15 | private final int threshold; 16 | private final PivotStrategy pivotStrategy; // just for the getName() method 17 | private final DualPivotQuicksort standardQuicksort; 18 | private final InsertionSort insertionSort; 19 | 20 | /** 21 | * Constructs the Dual-pivot Quicksort instance. 22 | * 23 | * @param threshold when the array to be sorted is not longer than this threshold, the algorithm 24 | * switches to Insertion Sort 25 | * @param pivotStrategy the pivot strategy to use 26 | */ 27 | public DualPivotQuicksortImproved(int threshold, PivotStrategy pivotStrategy) { 28 | this.threshold = threshold; 29 | this.pivotStrategy = pivotStrategy; 30 | this.standardQuicksort = new DualPivotQuicksort(pivotStrategy); 31 | this.insertionSort = new InsertionSort(); 32 | } 33 | 34 | @Override 35 | public String getName() { 36 | return this.getClass().getSimpleName() 37 | + "(threshold: " 38 | + threshold 39 | + ", pivot: " 40 | + pivotStrategy 41 | + ")"; 42 | } 43 | 44 | @Override 45 | public void sort(int[] elements) { 46 | quicksort(elements, 0, elements.length - 1); 47 | } 48 | 49 | private void quicksort(int[] elements, int left, int right) { 50 | // End of recursion reached? 51 | if (left >= right) { 52 | return; 53 | } 54 | 55 | // Threshold for insertion sort reached? 56 | if (right - left < threshold) { 57 | insertionSort.sort(elements, left, right + 1); 58 | return; 59 | } 60 | 61 | int[] pivotPos = standardQuicksort.partition(elements, left, right); 62 | int p0 = pivotPos[0]; 63 | int p1 = pivotPos[1]; 64 | quicksort(elements, left, p0 - 1); 65 | quicksort(elements, p0 + 1, p1 - 1); 66 | quicksort(elements, p1 + 1, right); 67 | } 68 | 69 | @Override 70 | public void sortWithCounters(int[] elements, Counters counters) { 71 | quicksortWithCounters(elements, 0, elements.length - 1, counters); 72 | } 73 | 74 | private void quicksortWithCounters(int[] elements, int left, int right, Counters counters) { 75 | // End of recursion reached? 76 | if (left >= right) { 77 | return; 78 | } 79 | 80 | // Threshold for insertion sort reached? 81 | if (right - left < threshold) { 82 | insertionSort.sortWithCounters(elements, left, right + 1, counters); 83 | return; 84 | } 85 | 86 | int[] pivotPos = standardQuicksort.partitionWithCounters(elements, left, right, counters); 87 | int p0 = pivotPos[0]; 88 | int p1 = pivotPos[1]; 89 | quicksortWithCounters(elements, left, p0 - 1, counters); 90 | quicksortWithCounters(elements, p0 + 1, p1 - 1, counters); 91 | quicksortWithCounters(elements, p1 + 1, right, counters); 92 | } 93 | 94 | @Override 95 | public boolean isSuitableForInputSize(int size) { 96 | return standardQuicksort.isSuitableForInputSize(size); 97 | } 98 | 99 | @Override 100 | public boolean isSuitableForSortedInput(int size) { 101 | return standardQuicksort.isSuitableForSortedInput(size); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/sort/demos/pivot/PivotComparator.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.demos.pivot; 2 | 3 | import eu.happycoders.sort.demos.Scorecard; 4 | import eu.happycoders.sort.method.PartitioningAlgorithm; 5 | import eu.happycoders.sort.method.SortAlgorithm; 6 | import eu.happycoders.sort.method.quicksort.PivotStrategy; 7 | import eu.happycoders.sort.method.quicksort.QuicksortVariant1; 8 | import eu.happycoders.sort.utils.ArrayUtils; 9 | import java.util.HashMap; 10 | import java.util.Locale; 11 | import java.util.Map; 12 | import java.util.concurrent.ThreadLocalRandom; 13 | 14 | /** 15 | * Compares several pivot strategies: in how many samples will we have a specific distribution of 16 | * elements (1.5:1, 2:1, 3:1) or better? 17 | * 18 | * @author Sven Woltmann 19 | */ 20 | public class PivotComparator { 21 | 22 | private static final int ITERATIONS = 500_000; 23 | private static final int MIN_SIZE = 500; 24 | private static final int MAX_SIZE = 1_000; 25 | 26 | @SuppressWarnings("PMD.UseConcurrentHashMap") // Not accessed concurrently 27 | private final Map scorecards = new HashMap<>(); 28 | 29 | public static void main(String[] args) { 30 | new PivotComparator().run(); 31 | } 32 | 33 | private void run() { 34 | PartitioningAlgorithm[] algorithms = { 35 | new QuicksortVariant1(PivotStrategy.MIDDLE), 36 | new QuicksortVariant1(PivotStrategy.RANDOM), 37 | new QuicksortVariant1(PivotStrategy.RIGHT), 38 | new QuicksortVariant1(PivotStrategy.MEDIAN3) 39 | }; 40 | 41 | runTest(algorithms); 42 | } 43 | 44 | @SuppressWarnings({"PMD.SystemPrintln", "java:S106"}) 45 | private void runTest(PartitioningAlgorithm[] algorithms) { 46 | int longestNameLength = Scorecard.findLongestAlgorithmName(algorithms); 47 | 48 | int numAlgorithms = algorithms.length; 49 | ThreadLocalRandom rand = ThreadLocalRandom.current(); 50 | 51 | for (int i = 1; i <= ITERATIONS; i++) { 52 | if (i % 10_000 == 0) { 53 | System.out.printf(Locale.US, "Iteration %,d%n", i); 54 | } 55 | 56 | // Check the same elements once with each algorithm 57 | int[] elements = ArrayUtils.createRandomArray(rand.nextInt(MIN_SIZE, MAX_SIZE)); 58 | 59 | for (int j = 0; j < numAlgorithms; j++) { 60 | PartitioningAlgorithm algorithm = algorithms[j]; 61 | double largerPartPercentage = partition(algorithm, elements.clone()); 62 | scorecardForAlgorithm(algorithm).add(largerPartPercentage); 63 | } 64 | } 65 | 66 | System.out.printf( 67 | Locale.US, "%n---------- Results after %,d iterations ----------%n", ITERATIONS); 68 | for (int j = 0; j < numAlgorithms; j++) { 69 | PivotScorecard scorecard = scorecardForAlgorithm(algorithms[j]); 70 | scorecard.printResult(longestNameLength); 71 | } 72 | } 73 | 74 | /** 75 | * Partitions the elements with the given sort algorithm and calculates the percentage of the 76 | * larger partition. 77 | * 78 | * @param algorithm the algorithm 79 | * @param elements the elements 80 | * @return the percentage of the larger partition; e.g., if we get 100 and 50 elements, the result 81 | * is 66.7. 82 | */ 83 | private double partition(PartitioningAlgorithm algorithm, int[] elements) { 84 | int numElements = elements.length; 85 | int pivotPos = algorithm.partition(elements, 0, numElements - 1); 86 | 87 | int longerPartSize = pivotPos >= numElements / 2 ? pivotPos : numElements - 1 - pivotPos; 88 | 89 | return longerPartSize * 100.0 / (numElements - 1); 90 | } 91 | 92 | private PivotScorecard scorecardForAlgorithm(SortAlgorithm algorithm) { 93 | return scorecards.computeIfAbsent(algorithm.getName(), PivotScorecard::new); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/sort/method/radixsort/RadixSortWithArraysAndCustomBase.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.radixsort; 2 | 3 | import static eu.happycoders.sort.method.radixsort.RadixSortHelper.calculateDivisor; 4 | import static eu.happycoders.sort.method.radixsort.RadixSortHelper.checkIfContainsNegatives; 5 | import static eu.happycoders.sort.method.radixsort.RadixSortHelper.getNumberOfDigits; 6 | import static eu.happycoders.sort.utils.ArrayUtils.getMaximum; 7 | 8 | import eu.happycoders.sort.method.Counters; 9 | import eu.happycoders.sort.method.SortAlgorithm; 10 | import eu.happycoders.sort.utils.NotImplementedException; 11 | 12 | /** 13 | * Radix Sort implementation using arrays as buckets and with a customizable base. 14 | * 15 | * @author Sven Woltmann 16 | */ 17 | public class RadixSortWithArraysAndCustomBase implements SortAlgorithm { 18 | 19 | private final int base; 20 | 21 | public RadixSortWithArraysAndCustomBase(int base) { 22 | this.base = base; 23 | } 24 | 25 | @Override 26 | public void sort(int[] elements) { 27 | checkIfContainsNegatives(elements); 28 | int max = getMaximum(elements); 29 | int numberOfDigits = getNumberOfDigits(max, base); 30 | 31 | for (int digitIndex = 0; digitIndex < numberOfDigits; digitIndex++) { 32 | sortByDigit(elements, digitIndex); 33 | } 34 | } 35 | 36 | private void sortByDigit(int[] elements, int digitIndex) { 37 | Bucket[] buckets = partition(elements, digitIndex); 38 | collect(buckets, elements); 39 | } 40 | 41 | private Bucket[] partition(int[] elements, int digitIndex) { 42 | int[] counts = countDigits(elements, digitIndex); 43 | Bucket[] buckets = createBuckets(counts); 44 | distributeToBuckets(elements, digitIndex, buckets); 45 | return buckets; 46 | } 47 | 48 | private int[] countDigits(int[] elements, int digitIndex) { 49 | int[] counts = new int[base]; 50 | int divisor = calculateDivisor(digitIndex, base); 51 | for (int element : elements) { 52 | int digit = element / divisor % base; 53 | counts[digit]++; 54 | } 55 | return counts; 56 | } 57 | 58 | private Bucket[] createBuckets(int[] counts) { 59 | Bucket[] buckets = new Bucket[base]; 60 | for (int i = 0; i < base; i++) { 61 | buckets[i] = new Bucket(counts[i]); 62 | } 63 | return buckets; 64 | } 65 | 66 | private void distributeToBuckets(int[] elements, int digitIndex, Bucket[] buckets) { 67 | int divisor = calculateDivisor(digitIndex, base); 68 | 69 | for (int element : elements) { 70 | int digit = element / divisor % base; 71 | buckets[digit].add(element); 72 | } 73 | } 74 | 75 | private void collect(Bucket[] buckets, int[] elements) { 76 | int targetIndex = 0; 77 | for (Bucket bucket : buckets) { 78 | for (int element : bucket.getElements()) { 79 | elements[targetIndex] = element; 80 | targetIndex++; 81 | } 82 | } 83 | } 84 | 85 | @Override 86 | public void sortWithCounters(int[] elements, Counters counters) { 87 | throw new NotImplementedException(); 88 | } 89 | 90 | @Override 91 | public String getName() { 92 | return this.getClass().getSimpleName() + "(" + base + ")"; 93 | } 94 | 95 | @Override 96 | public boolean supportsCounting() { 97 | return false; 98 | } 99 | 100 | private static class Bucket { 101 | private final int[] elements; 102 | private int addIndex; 103 | 104 | private Bucket(int size) { 105 | elements = new int[size]; 106 | } 107 | 108 | private void add(int element) { 109 | elements[addIndex] = element; 110 | addIndex++; 111 | } 112 | 113 | private int[] getElements() { 114 | return elements; 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/sort/method/radixsort/RecursiveMsdRadixSortWithArrays.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.radixsort; 2 | 3 | import static eu.happycoders.sort.method.radixsort.RadixSortHelper.calculateDivisor; 4 | import static eu.happycoders.sort.method.radixsort.RadixSortHelper.checkIfContainsNegatives; 5 | import static eu.happycoders.sort.method.radixsort.RadixSortHelper.getNumberOfDigits; 6 | import static eu.happycoders.sort.utils.ArrayUtils.getMaximum; 7 | 8 | import eu.happycoders.sort.method.Counters; 9 | import eu.happycoders.sort.method.SortAlgorithm; 10 | import eu.happycoders.sort.utils.NotImplementedException; 11 | 12 | /** 13 | * Recursive MSD (most significant digit) Radix Sort implementation using arrays as buckets. 14 | * 15 | * @author Sven Woltmann 16 | */ 17 | public class RecursiveMsdRadixSortWithArrays implements SortAlgorithm { 18 | 19 | @Override 20 | public void sort(int[] elements) { 21 | checkIfContainsNegatives(elements); 22 | int max = getMaximum(elements); 23 | int numberOfDigits = getNumberOfDigits(max); 24 | 25 | sortByDigit(elements, numberOfDigits - 1); 26 | } 27 | 28 | private void sortByDigit(int[] elements, int digitIndex) { 29 | Bucket[] buckets = partition(elements, digitIndex); 30 | 31 | // If we haven't reached the last digit, sort the buckets by the next digit, recursively 32 | if (digitIndex > 0) { 33 | for (Bucket bucket : buckets) { 34 | if (bucket.needsToBeSorted()) { 35 | sortByDigit(bucket.getElements(), digitIndex - 1); 36 | } 37 | } 38 | } 39 | 40 | collect(buckets, elements); 41 | } 42 | 43 | private Bucket[] partition(int[] elements, int digitIndex) { 44 | int[] counts = countDigits(elements, digitIndex); 45 | Bucket[] buckets = createBuckets(counts); 46 | distributeToBuckets(elements, digitIndex, buckets); 47 | return buckets; 48 | } 49 | 50 | private int[] countDigits(int[] elements, int digitIndex) { 51 | int[] counts = new int[10]; 52 | int divisor = calculateDivisor(digitIndex); 53 | for (int element : elements) { 54 | int digit = element / divisor % 10; 55 | counts[digit]++; 56 | } 57 | return counts; 58 | } 59 | 60 | private Bucket[] createBuckets(int[] counts) { 61 | Bucket[] buckets = new Bucket[10]; 62 | for (int i = 0; i < 10; i++) { 63 | buckets[i] = new Bucket(counts[i]); 64 | } 65 | return buckets; 66 | } 67 | 68 | private void distributeToBuckets(int[] elements, int digitIndex, Bucket[] buckets) { 69 | int divisor = calculateDivisor(digitIndex); 70 | 71 | for (int element : elements) { 72 | int digit = element / divisor % 10; 73 | buckets[digit].add(element); 74 | } 75 | } 76 | 77 | private void collect(Bucket[] buckets, int[] elements) { 78 | int targetIndex = 0; 79 | for (Bucket bucket : buckets) { 80 | for (int element : bucket.getElements()) { 81 | elements[targetIndex] = element; 82 | targetIndex++; 83 | } 84 | } 85 | } 86 | 87 | @Override 88 | public void sortWithCounters(int[] elements, Counters counters) { 89 | throw new NotImplementedException(); 90 | } 91 | 92 | @Override 93 | public boolean supportsCounting() { 94 | return false; 95 | } 96 | 97 | private static class Bucket { 98 | private final int[] elements; 99 | private int addIndex; 100 | 101 | private Bucket(int size) { 102 | elements = new int[size]; 103 | } 104 | 105 | private void add(int element) { 106 | elements[addIndex] = element; 107 | addIndex++; 108 | } 109 | 110 | private int[] getElements() { 111 | return elements; 112 | } 113 | 114 | private boolean needsToBeSorted() { 115 | return elements.length > 1; 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/sort/method/radixsort/ParallelRecursiveMsdRadixSortWithArrays.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.radixsort; 2 | 3 | import static eu.happycoders.sort.method.radixsort.RadixSortHelper.calculateDivisor; 4 | import static eu.happycoders.sort.method.radixsort.RadixSortHelper.checkIfContainsNegatives; 5 | import static eu.happycoders.sort.method.radixsort.RadixSortHelper.getNumberOfDigits; 6 | import static eu.happycoders.sort.utils.ArrayUtils.getMaximum; 7 | 8 | import eu.happycoders.sort.method.Counters; 9 | import eu.happycoders.sort.method.SortAlgorithm; 10 | import eu.happycoders.sort.utils.NotImplementedException; 11 | import java.util.Arrays; 12 | 13 | /** 14 | * Parallel recursive MSD (most significant digit) Radix Sort implementation using arrays as 15 | * buckets. 16 | * 17 | * @author Sven Woltmann 18 | */ 19 | public class ParallelRecursiveMsdRadixSortWithArrays implements SortAlgorithm { 20 | 21 | @Override 22 | public void sort(int[] elements) { 23 | checkIfContainsNegatives(elements); 24 | int max = getMaximum(elements); 25 | int numberOfDigits = getNumberOfDigits(max); 26 | 27 | sortByDigit(elements, numberOfDigits - 1); 28 | } 29 | 30 | private void sortByDigit(int[] elements, int digitIndex) { 31 | Bucket[] buckets = partition(elements, digitIndex); 32 | 33 | // If we haven't reached the last digit, sort the buckets by the next digit, recursively 34 | if (digitIndex > 0) { 35 | Arrays.stream(buckets) 36 | .parallel() 37 | .forEach( 38 | bucket -> { 39 | if (bucket.needsToBeSorted()) { 40 | sortByDigit(bucket.getElements(), digitIndex - 1); 41 | } 42 | }); 43 | } 44 | 45 | collect(buckets, elements); 46 | } 47 | 48 | private Bucket[] partition(int[] elements, int digitIndex) { 49 | int[] counts = countDigits(elements, digitIndex); 50 | Bucket[] buckets = createBuckets(counts); 51 | distributeToBuckets(elements, digitIndex, buckets); 52 | return buckets; 53 | } 54 | 55 | private int[] countDigits(int[] elements, int digitIndex) { 56 | int[] counts = new int[10]; 57 | int divisor = calculateDivisor(digitIndex); 58 | for (int element : elements) { 59 | int digit = element / divisor % 10; 60 | counts[digit]++; 61 | } 62 | return counts; 63 | } 64 | 65 | private Bucket[] createBuckets(int[] counts) { 66 | Bucket[] buckets = new Bucket[10]; 67 | for (int i = 0; i < 10; i++) { 68 | buckets[i] = new Bucket(counts[i]); 69 | } 70 | return buckets; 71 | } 72 | 73 | private void distributeToBuckets(int[] elements, int digitIndex, Bucket[] buckets) { 74 | int divisor = calculateDivisor(digitIndex); 75 | 76 | for (int element : elements) { 77 | int digit = element / divisor % 10; 78 | buckets[digit].add(element); 79 | } 80 | } 81 | 82 | private void collect(Bucket[] buckets, int[] elements) { 83 | int targetIndex = 0; 84 | for (Bucket bucket : buckets) { 85 | for (int element : bucket.getElements()) { 86 | elements[targetIndex] = element; 87 | targetIndex++; 88 | } 89 | } 90 | } 91 | 92 | @Override 93 | public void sortWithCounters(int[] elements, Counters counters) { 94 | throw new NotImplementedException(); 95 | } 96 | 97 | @Override 98 | public boolean supportsCounting() { 99 | return false; 100 | } 101 | 102 | private static class Bucket { 103 | private final int[] elements; 104 | private int addIndex; 105 | 106 | private Bucket(int size) { 107 | elements = new int[size]; 108 | } 109 | 110 | private void add(int element) { 111 | elements[addIndex] = element; 112 | addIndex++; 113 | } 114 | 115 | private int[] getElements() { 116 | return elements; 117 | } 118 | 119 | private boolean needsToBeSorted() { 120 | return elements.length > 1; 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/quicksort/QuicksortVariant1PartitionTest.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.quicksort; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertTrue; 4 | 5 | import eu.happycoders.sort.utils.ArrayUtils; 6 | import java.util.concurrent.ThreadLocalRandom; 7 | import java.util.function.Function; 8 | import org.junit.jupiter.api.*; 9 | 10 | class QuicksortVariant1PartitionTest { 11 | 12 | // Unfortunately, we cannot combine @RepeatedTest with @ParameterizedTest 13 | 14 | // ---- PivotStrategy.RANDOM ---- 15 | 16 | @RepeatedTest(100) 17 | void partitionRandom_randomArray_partitioned() { 18 | testPartitioning(PivotStrategy.RANDOM, ArrayUtils::createRandomArray); 19 | } 20 | 21 | @RepeatedTest(100) 22 | void partitionRandom_sortedArray_partitioned() { 23 | testPartitioning(PivotStrategy.RANDOM, ArrayUtils::createSortedArray); 24 | } 25 | 26 | @RepeatedTest(100) 27 | void partitionRandom_reversedArray_partitioned() { 28 | testPartitioning(PivotStrategy.RANDOM, ArrayUtils::createReversedArray); 29 | } 30 | 31 | // ---- PivotStrategy.LEFT ---- 32 | 33 | @RepeatedTest(100) 34 | void partitionLEFT_randomArray_partitioned() { 35 | testPartitioning(PivotStrategy.LEFT, ArrayUtils::createRandomArray); 36 | } 37 | 38 | @Test 39 | void partitionLEFT_sortedArray_partitioned() { 40 | testPartitioning(PivotStrategy.LEFT, ArrayUtils::createSortedArray); 41 | } 42 | 43 | @Test 44 | void partitionLEFT_reversedArray_partitioned() { 45 | testPartitioning(PivotStrategy.LEFT, ArrayUtils::createReversedArray); 46 | } 47 | 48 | // ---- PivotStrategy.RIGHT ---- 49 | 50 | @RepeatedTest(100) 51 | void partitionRIGHT_randomArray_partitioned() { 52 | testPartitioning(PivotStrategy.RIGHT, ArrayUtils::createRandomArray); 53 | } 54 | 55 | @Test 56 | void partitionRIGHT_sortedArray_partitioned() { 57 | testPartitioning(PivotStrategy.RIGHT, ArrayUtils::createSortedArray); 58 | } 59 | 60 | @Test 61 | void partitionRIGHT_reversedArray_partitioned() { 62 | testPartitioning(PivotStrategy.RIGHT, ArrayUtils::createReversedArray); 63 | } 64 | 65 | // ---- PivotStrategy.MIDDLE ---- 66 | 67 | @RepeatedTest(100) 68 | void partitionMIDDLE_randomArray_partitioned() { 69 | testPartitioning(PivotStrategy.MIDDLE, ArrayUtils::createRandomArray); 70 | } 71 | 72 | @Test 73 | void partitionMIDDLE_sortedArray_partitioned() { 74 | testPartitioning(PivotStrategy.MIDDLE, ArrayUtils::createSortedArray); 75 | } 76 | 77 | @Test 78 | void partitionMIDDLE_reversedArray_partitioned() { 79 | testPartitioning(PivotStrategy.MIDDLE, ArrayUtils::createReversedArray); 80 | } 81 | 82 | // ---- PivotStrategy.MEDIAN3 ---- 83 | 84 | @RepeatedTest(100) 85 | void partitionMEDIAN3_randomArray_partitioned() { 86 | testPartitioning(PivotStrategy.MEDIAN3, ArrayUtils::createRandomArray); 87 | } 88 | 89 | @Test 90 | void partitionMEDIAN3_sortedArray_partitioned() { 91 | testPartitioning(PivotStrategy.MEDIAN3, ArrayUtils::createSortedArray); 92 | } 93 | 94 | @Test 95 | void partitionMEDIAN3_reversedArray_partitioned() { 96 | testPartitioning(PivotStrategy.MEDIAN3, ArrayUtils::createReversedArray); 97 | } 98 | 99 | // ---- private methods ---- 100 | 101 | private void testPartitioning(PivotStrategy pivotMode, Function arraySupplier) { 102 | int[] elements = arraySupplier.apply(randomSize()); 103 | int pivotPos = new QuicksortVariant1(pivotMode).partition(elements, 0, elements.length - 1); 104 | 105 | assertTrue(pivotPos >= 0); 106 | assertTrue(pivotPos < elements.length); 107 | 108 | // All elements before pivotPos must be <= pivot element 109 | int pivotElement = elements[pivotPos]; 110 | for (int i = 0; i < pivotPos; i++) { 111 | assertTrue(elements[i] <= pivotElement); 112 | } 113 | // All elements after pivotPos must be >= pivot element 114 | for (int i = pivotPos + 1; i < elements.length; i++) { 115 | assertTrue(elements[i] >= pivotElement); 116 | } 117 | } 118 | 119 | private int randomSize() { 120 | return ThreadLocalRandom.current().nextInt(2, 10_000); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/quicksort/QuicksortVariant2PartitionTest.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.quicksort; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertTrue; 4 | 5 | import eu.happycoders.sort.utils.ArrayUtils; 6 | import java.util.concurrent.ThreadLocalRandom; 7 | import java.util.function.Function; 8 | import org.junit.jupiter.api.*; 9 | 10 | class QuicksortVariant2PartitionTest { 11 | 12 | // Unfortunately, we cannot combine @RepeatedTest with @ParameterizedTest 13 | 14 | // ---- PivotStrategy.RANDOM ---- 15 | 16 | @RepeatedTest(100) 17 | void partitionRandom_randomArray_partitioned() { 18 | testPartitioning(PivotStrategy.RANDOM, ArrayUtils::createRandomArray); 19 | } 20 | 21 | @RepeatedTest(100) 22 | void partitionRandom_sortedArray_partitioned() { 23 | testPartitioning(PivotStrategy.RANDOM, ArrayUtils::createSortedArray); 24 | } 25 | 26 | @RepeatedTest(100) 27 | void partitionRandom_reversedArray_partitioned() { 28 | testPartitioning(PivotStrategy.RANDOM, ArrayUtils::createReversedArray); 29 | } 30 | 31 | // ---- PivotStrategy.LEFT ---- 32 | 33 | @RepeatedTest(100) 34 | void partitionLEFT_randomArray_partitioned() { 35 | testPartitioning(PivotStrategy.LEFT, ArrayUtils::createRandomArray); 36 | } 37 | 38 | @Test 39 | void partitionLEFT_sortedArray_partitioned() { 40 | testPartitioning(PivotStrategy.LEFT, ArrayUtils::createSortedArray); 41 | } 42 | 43 | @Test 44 | void partitionLEFT_reversedArray_partitioned() { 45 | testPartitioning(PivotStrategy.LEFT, ArrayUtils::createReversedArray); 46 | } 47 | 48 | // ---- PivotStrategy.RIGHT ---- 49 | 50 | @RepeatedTest(100) 51 | void partitionRIGHT_randomArray_partitioned() { 52 | testPartitioning(PivotStrategy.RIGHT, ArrayUtils::createRandomArray); 53 | } 54 | 55 | @Test 56 | void partitionRIGHT_sortedArray_partitioned() { 57 | testPartitioning(PivotStrategy.RIGHT, ArrayUtils::createSortedArray); 58 | } 59 | 60 | @Test 61 | void partitionRIGHT_reversedArray_partitioned() { 62 | testPartitioning(PivotStrategy.RIGHT, ArrayUtils::createReversedArray); 63 | } 64 | 65 | // ---- PivotStrategy.MIDDLE ---- 66 | 67 | @RepeatedTest(100) 68 | void partitionMIDDLE_randomArray_partitioned() { 69 | testPartitioning(PivotStrategy.MIDDLE, ArrayUtils::createRandomArray); 70 | } 71 | 72 | @Test 73 | void partitionMIDDLE_sortedArray_partitioned() { 74 | testPartitioning(PivotStrategy.MIDDLE, ArrayUtils::createSortedArray); 75 | } 76 | 77 | @Test 78 | void partitionMIDDLE_reversedArray_partitioned() { 79 | testPartitioning(PivotStrategy.MIDDLE, ArrayUtils::createReversedArray); 80 | } 81 | 82 | // ---- PivotStrategy.MEDIAN3 ---- 83 | 84 | @RepeatedTest(100) 85 | void partitionMEDIAN3_randomArray_partitioned() { 86 | testPartitioning(PivotStrategy.MEDIAN3, ArrayUtils::createRandomArray); 87 | } 88 | 89 | @Test 90 | void partitionMEDIAN3_sortedArray_partitioned() { 91 | testPartitioning(PivotStrategy.MEDIAN3, ArrayUtils::createSortedArray); 92 | } 93 | 94 | @Test 95 | void partitionMEDIAN3_reversedArray_partitioned() { 96 | testPartitioning(PivotStrategy.MEDIAN3, ArrayUtils::createReversedArray); 97 | } 98 | 99 | // ---- private methods ---- 100 | 101 | private void testPartitioning(PivotStrategy pivotMode, Function arraySupplier) { 102 | int[] elements = arraySupplier.apply(randomSize()); 103 | int pivotPos = new QuicksortVariant2(pivotMode).partition(elements, 0, elements.length - 1); 104 | 105 | assertTrue(pivotPos >= 0); 106 | assertTrue(pivotPos < elements.length); 107 | 108 | // All elements before pivotPos must be <= pivot element 109 | int pivotElement = elements[pivotPos]; 110 | for (int i = 0; i < pivotPos; i++) { 111 | assertTrue(elements[i] <= pivotElement); 112 | } 113 | // All elements after pivotPos must be >= pivot element 114 | for (int i = pivotPos + 1; i < elements.length; i++) { 115 | assertTrue(elements[i] >= pivotElement); 116 | } 117 | } 118 | 119 | private int randomSize() { 120 | return ThreadLocalRandom.current().nextInt(2, 10_000); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/test/java/eu/happycoders/sort/method/quicksort/QuicksortVariant3PartitionTest.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.quicksort; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertTrue; 4 | 5 | import eu.happycoders.sort.utils.ArrayUtils; 6 | import java.util.concurrent.ThreadLocalRandom; 7 | import java.util.function.Function; 8 | import org.junit.jupiter.api.*; 9 | 10 | class QuicksortVariant3PartitionTest { 11 | 12 | // Unfortunately, we cannot combine @RepeatedTest with @ParameterizedTest 13 | 14 | // ---- PivotStrategy.RANDOM ---- 15 | 16 | @RepeatedTest(100) 17 | void partitionRandom_randomArray_partitioned() { 18 | testPartitioning(PivotStrategy.RANDOM, ArrayUtils::createRandomArray); 19 | } 20 | 21 | @RepeatedTest(100) 22 | void partitionRandom_sortedArray_partitioned() { 23 | testPartitioning(PivotStrategy.RANDOM, ArrayUtils::createSortedArray); 24 | } 25 | 26 | @RepeatedTest(100) 27 | void partitionRandom_reversedArray_partitioned() { 28 | testPartitioning(PivotStrategy.RANDOM, ArrayUtils::createReversedArray); 29 | } 30 | 31 | // ---- PivotStrategy.LEFT ---- 32 | 33 | @RepeatedTest(100) 34 | void partitionLEFT_randomArray_partitioned() { 35 | testPartitioning(PivotStrategy.LEFT, ArrayUtils::createRandomArray); 36 | } 37 | 38 | @Test 39 | void partitionLEFT_sortedArray_partitioned() { 40 | testPartitioning(PivotStrategy.LEFT, ArrayUtils::createSortedArray); 41 | } 42 | 43 | @Test 44 | void partitionLEFT_reversedArray_partitioned() { 45 | testPartitioning(PivotStrategy.LEFT, ArrayUtils::createReversedArray); 46 | } 47 | 48 | // ---- PivotStrategy.RIGHT ---- 49 | 50 | @RepeatedTest(100) 51 | void partitionRIGHT_randomArray_partitioned() { 52 | testPartitioning(PivotStrategy.RIGHT, ArrayUtils::createRandomArray); 53 | } 54 | 55 | @Test 56 | void partitionRIGHT_sortedArray_partitioned() { 57 | testPartitioning(PivotStrategy.RIGHT, ArrayUtils::createSortedArray); 58 | } 59 | 60 | @Test 61 | void partitionRIGHT_reversedArray_partitioned() { 62 | testPartitioning(PivotStrategy.RIGHT, ArrayUtils::createReversedArray); 63 | } 64 | 65 | // ---- PivotStrategy.MIDDLE ---- 66 | 67 | @RepeatedTest(100) 68 | void partitionMIDDLE_randomArray_partitioned() { 69 | testPartitioning(PivotStrategy.MIDDLE, ArrayUtils::createRandomArray); 70 | } 71 | 72 | @Test 73 | void partitionMIDDLE_sortedArray_partitioned() { 74 | testPartitioning(PivotStrategy.MIDDLE, ArrayUtils::createSortedArray); 75 | } 76 | 77 | @Test 78 | void partitionMIDDLE_reversedArray_partitioned() { 79 | testPartitioning(PivotStrategy.MIDDLE, ArrayUtils::createReversedArray); 80 | } 81 | 82 | // ---- PivotStrategy.MEDIAN3 ---- 83 | 84 | @RepeatedTest(100) 85 | void partitionMEDIAN3_randomArray_partitioned() { 86 | testPartitioning(PivotStrategy.MEDIAN3, ArrayUtils::createRandomArray); 87 | } 88 | 89 | @Test 90 | void partitionMEDIAN3_sortedArray_partitioned() { 91 | testPartitioning(PivotStrategy.MEDIAN3, ArrayUtils::createSortedArray); 92 | } 93 | 94 | @Test 95 | void partitionMEDIAN3_reversedArray_partitioned() { 96 | testPartitioning(PivotStrategy.MEDIAN3, ArrayUtils::createReversedArray); 97 | } 98 | 99 | // ---- private methods ---- 100 | 101 | private void testPartitioning(PivotStrategy pivotMode, Function arraySupplier) { 102 | int[] elements = arraySupplier.apply(randomSize()); 103 | int pivotPos = new QuicksortVariant3(pivotMode).partition(elements, 0, elements.length - 1); 104 | 105 | assertTrue(pivotPos >= 0); 106 | assertTrue(pivotPos < elements.length); 107 | 108 | // All elements before pivotPos must be <= pivot element 109 | int pivotElement = elements[pivotPos]; 110 | for (int i = 0; i < pivotPos; i++) { 111 | assertTrue(elements[i] <= pivotElement); 112 | } 113 | // All elements after pivotPos must be >= pivot element 114 | for (int i = pivotPos + 1; i < elements.length; i++) { 115 | assertTrue(elements[i] >= pivotElement); 116 | } 117 | } 118 | 119 | private int randomSize() { 120 | return ThreadLocalRandom.current().nextInt(2, 10_000); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/sort/method/mergesort/InPlaceMergeSort.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.mergesort; 2 | 3 | import eu.happycoders.sort.method.Counters; 4 | import eu.happycoders.sort.method.SortAlgorithm; 5 | 6 | /** 7 | * Trivial In-place Merge sort implementation for performance tests. 8 | * 9 | *

This implementation has a space complexity of O(1), however its time complexity is O(n² log n) 10 | * due to the two nested loops in the merge() method. 11 | * 12 | * @author Sven Woltmann 13 | */ 14 | // We don't want to use System.arraycopy - we want to demonstrate how the algorithm works 15 | @SuppressWarnings("PMD.AvoidArrayLoops") 16 | public class InPlaceMergeSort implements SortAlgorithm { 17 | 18 | @Override 19 | public void sort(int[] elements) { 20 | int length = elements.length; 21 | mergeSort(elements, 0, length - 1); 22 | } 23 | 24 | private void mergeSort(int[] elements, int left, int right) { 25 | // End of recursion reached? 26 | if (left == right) { 27 | return; 28 | } 29 | 30 | int middle = left + (right - left) / 2; 31 | mergeSort(elements, left, middle); 32 | mergeSort(elements, middle + 1, right); 33 | merge(elements, left, middle + 1, right); 34 | } 35 | 36 | void merge(int[] elements, int leftPos, int rightPos, int rightEnd) { 37 | int leftEnd = rightPos - 1; 38 | 39 | while (leftPos <= leftEnd && rightPos <= rightEnd) { 40 | // Which one is smaller? 41 | int leftValue = elements[leftPos]; 42 | int rightValue = elements[rightPos]; 43 | if (leftValue <= rightValue) { 44 | leftPos++; 45 | } else { 46 | // Move all the elements from leftPos to excluding rightPos one field 47 | // to the right 48 | int movePos = rightPos; 49 | while (movePos > leftPos) { 50 | elements[movePos] = elements[movePos - 1]; 51 | movePos--; 52 | } 53 | elements[leftPos] = rightValue; 54 | leftPos++; 55 | leftEnd++; 56 | rightPos++; 57 | } 58 | } 59 | } 60 | 61 | @Override 62 | public void sortWithCounters(int[] elements, Counters counters) { 63 | int length = elements.length; 64 | mergeSortWithCounters(elements, 0, length - 1, counters); 65 | } 66 | 67 | private void mergeSortWithCounters(int[] elements, int left, int right, Counters counters) { 68 | // End of recursion reached? 69 | if (left == right) { 70 | return; 71 | } 72 | 73 | int middle = left + (right - left) / 2; 74 | mergeSortWithCounters(elements, left, middle, counters); 75 | mergeSortWithCounters(elements, middle + 1, right, counters); 76 | mergeWithCounters(elements, left, middle + 1, right, counters); 77 | } 78 | 79 | private void mergeWithCounters( 80 | int[] elements, int leftPos, int rightPos, int rightEnd, Counters counters) { 81 | int leftEnd = rightPos - 1; 82 | 83 | while (isLessThanOrEqual(leftPos, leftEnd, counters) 84 | && isLessThanOrEqual(rightPos, rightEnd, counters)) { 85 | // Which one is smaller? 86 | int leftValue = elements[leftPos]; 87 | int rightValue = elements[rightPos]; 88 | counters.addReads(2); 89 | 90 | counters.incComparisons(); 91 | if (leftValue <= rightValue) { 92 | leftPos++; 93 | } else { 94 | // Move all the elements from leftPos to excluding rightPos one field 95 | // to the right 96 | int movePos = rightPos; 97 | while (isGreaterThan(movePos, leftPos, counters)) { 98 | counters.incReadsAndWrites(); 99 | elements[movePos] = elements[movePos - 1]; 100 | movePos--; 101 | } 102 | counters.incWrites(); 103 | elements[leftPos] = rightValue; 104 | leftPos++; 105 | leftEnd++; 106 | rightPos++; 107 | } 108 | } 109 | } 110 | 111 | private boolean isLessThanOrEqual(int a, int b, Counters counters) { 112 | counters.incComparisons(); 113 | return a <= b; 114 | } 115 | 116 | private boolean isGreaterThan(int a, int b, Counters counters) { 117 | counters.incComparisons(); 118 | return a > b; 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/sort/method/bubblesort/BubbleSortParallel.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.bubblesort; 2 | 3 | import eu.happycoders.sort.method.Counters; 4 | import eu.happycoders.sort.method.SortAlgorithm; 5 | import eu.happycoders.sort.utils.NotImplementedException; 6 | import java.util.concurrent.Phaser; 7 | import java.util.concurrent.atomic.AtomicInteger; 8 | 9 | /** 10 | * Abstract base class for parallel Bubble Sort implementations using partitions. 11 | * 12 | * @author Sven Woltmann 13 | */ 14 | public abstract class BubbleSortParallel implements SortAlgorithm { 15 | 16 | @Override 17 | public void sort(int[] elements) { 18 | AtomicInteger lastSwappedInRound = new AtomicInteger(); 19 | 20 | int[] startPositions = partition(elements); 21 | int numThreads = startPositions.length - 1; 22 | 23 | Phaser phaser = new Phaser(numThreads); 24 | Thread[] threads = new Thread[numThreads]; 25 | for (int i = 0; i < numThreads; i++) { 26 | int startPos = startPositions[i]; 27 | int endPos = startPositions[i + 1]; 28 | threads[i] = createThread(elements, startPos, endPos, lastSwappedInRound, phaser); 29 | } 30 | 31 | for (int i = 0; i < numThreads; i++) { 32 | threads[i].start(); 33 | } 34 | 35 | for (int i = 0; i < numThreads; i++) { 36 | try { 37 | threads[i].join(); 38 | } catch (InterruptedException e) { 39 | Thread.currentThread().interrupt(); 40 | } 41 | } 42 | } 43 | 44 | /** 45 | * Partitions the elements. 46 | * 47 | * @param elements the elements 48 | * @return an array of start positions; the array's length is one more than the number of 49 | * partitions; the last element contains the length of the array. 50 | */ 51 | private int[] partition(int[] elements) { 52 | int numPartitions = Math.min(Runtime.getRuntime().availableProcessors(), elements.length / 2); 53 | int remainingElements = elements.length; 54 | int remainingPartitions = numPartitions; 55 | int[] startPositions = new int[numPartitions + 1]; 56 | for (int i = 0; i < numPartitions; i++) { 57 | int partitionSize = remainingElements / remainingPartitions; 58 | if (isOdd(partitionSize) && startPositions[i] + partitionSize < elements.length) { 59 | partitionSize++; 60 | } 61 | 62 | remainingElements -= partitionSize; 63 | remainingPartitions--; 64 | startPositions[i + 1] = startPositions[i] + partitionSize; 65 | } 66 | return startPositions; 67 | } 68 | 69 | private boolean isOdd(int number) { 70 | return number % 2 != 0; 71 | } 72 | 73 | private Thread createThread( 74 | int[] elements, int startPos, int endPos, AtomicInteger lastSwappedInRound, Phaser phaser) { 75 | return new Thread( 76 | () -> { 77 | for (int round = 1; ; round++) { 78 | phaser.arriveAndAwaitAdvance(); 79 | 80 | boolean swapped = sortPartition(elements, startPos, endPos, false); 81 | 82 | phaser.arriveAndAwaitAdvance(); 83 | 84 | swapped |= sortPartition(elements, startPos, endPos, true); 85 | if (swapped) { 86 | lastSwappedInRound.set(round); 87 | } 88 | 89 | phaser.arriveAndAwaitAdvance(); 90 | 91 | if (lastSwappedInRound.get() < round) { 92 | break; 93 | } 94 | } 95 | }); 96 | } 97 | 98 | /** 99 | * Sorts a partition of the elements. 100 | * 101 | * @param elements the elements 102 | * @param startPos the partition's start position within the elements 103 | * @param endPos the partition's end position within the elements 104 | * @param even whether it's the even or odd step of an iteration 105 | * @return whether any elements were swapped 106 | */ 107 | abstract boolean sortPartition(int[] elements, int startPos, int endPos, boolean even); 108 | 109 | @Override 110 | public void sortWithCounters(int[] elements, Counters counters) { 111 | throw new NotImplementedException(); 112 | } 113 | 114 | @Override 115 | public boolean supportsCounting() { 116 | return false; 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/sort/method/radixsort/RecursiveMsdRadixSortWithArraysAndCustomBase.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.radixsort; 2 | 3 | import static eu.happycoders.sort.method.radixsort.RadixSortHelper.calculateDivisor; 4 | import static eu.happycoders.sort.method.radixsort.RadixSortHelper.checkIfContainsNegatives; 5 | import static eu.happycoders.sort.method.radixsort.RadixSortHelper.getNumberOfDigits; 6 | import static eu.happycoders.sort.utils.ArrayUtils.getMaximum; 7 | 8 | import eu.happycoders.sort.method.Counters; 9 | import eu.happycoders.sort.method.SortAlgorithm; 10 | import eu.happycoders.sort.utils.NotImplementedException; 11 | 12 | /** 13 | * Recursive MSD (most significant digit) Radix Sort implementation using arrays as buckets and with 14 | * a customizable base. 15 | * 16 | * @author Sven Woltmann 17 | */ 18 | public class RecursiveMsdRadixSortWithArraysAndCustomBase implements SortAlgorithm { 19 | 20 | private final int base; 21 | 22 | public RecursiveMsdRadixSortWithArraysAndCustomBase(int base) { 23 | this.base = base; 24 | } 25 | 26 | @Override 27 | public void sort(int[] elements) { 28 | checkIfContainsNegatives(elements); 29 | int max = getMaximum(elements); 30 | int numberOfDigits = getNumberOfDigits(max, base); 31 | 32 | sortByDigit(elements, numberOfDigits - 1); 33 | } 34 | 35 | private void sortByDigit(int[] elements, int digitIndex) { 36 | Bucket[] buckets = partition(elements, digitIndex); 37 | 38 | // If we haven't reached the last digit, sort the buckets by the next digit, recursively 39 | if (digitIndex > 0) { 40 | for (Bucket bucket : buckets) { 41 | if (bucket.needsToBeSorted()) { 42 | sortByDigit(bucket.getElements(), digitIndex - 1); 43 | } 44 | } 45 | } 46 | 47 | collect(buckets, elements); 48 | } 49 | 50 | private Bucket[] partition(int[] elements, int digitIndex) { 51 | int[] counts = countDigits(elements, digitIndex); 52 | Bucket[] buckets = createBuckets(counts); 53 | distributeToBuckets(elements, digitIndex, buckets); 54 | return buckets; 55 | } 56 | 57 | private int[] countDigits(int[] elements, int digitIndex) { 58 | int[] counts = new int[base]; 59 | int divisor = calculateDivisor(digitIndex, base); 60 | for (int element : elements) { 61 | int digit = element / divisor % base; 62 | counts[digit]++; 63 | } 64 | return counts; 65 | } 66 | 67 | private Bucket[] createBuckets(int[] counts) { 68 | Bucket[] buckets = new Bucket[base]; 69 | for (int i = 0; i < base; i++) { 70 | buckets[i] = new Bucket(counts[i]); 71 | } 72 | return buckets; 73 | } 74 | 75 | private void distributeToBuckets(int[] elements, int digitIndex, Bucket[] buckets) { 76 | int divisor = calculateDivisor(digitIndex, base); 77 | 78 | for (int element : elements) { 79 | int digit = element / divisor % base; 80 | buckets[digit].add(element); 81 | } 82 | } 83 | 84 | private void collect(Bucket[] buckets, int[] elements) { 85 | int targetIndex = 0; 86 | for (Bucket bucket : buckets) { 87 | for (int element : bucket.getElements()) { 88 | elements[targetIndex] = element; 89 | targetIndex++; 90 | } 91 | } 92 | } 93 | 94 | @Override 95 | public void sortWithCounters(int[] elements, Counters counters) { 96 | throw new NotImplementedException(); 97 | } 98 | 99 | @Override 100 | public String getName() { 101 | return this.getClass().getSimpleName() + "(" + base + ")"; 102 | } 103 | 104 | @Override 105 | public boolean supportsCounting() { 106 | return false; 107 | } 108 | 109 | private static class Bucket { 110 | private final int[] elements; 111 | private int addIndex; 112 | 113 | private Bucket(int size) { 114 | elements = new int[size]; 115 | } 116 | 117 | private void add(int element) { 118 | elements[addIndex] = element; 119 | addIndex++; 120 | } 121 | 122 | private int[] getElements() { 123 | return elements; 124 | } 125 | 126 | private boolean needsToBeSorted() { 127 | return elements.length > 1; 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /ruleset.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | PMD ruleset for HappyCoders.eu 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/sort/method/mergesort/MergeSort3.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.mergesort; 2 | 3 | import eu.happycoders.sort.method.Counters; 4 | import eu.happycoders.sort.method.SortAlgorithm; 5 | import java.util.Arrays; 6 | 7 | /** 8 | * Merge sort implementation for performance tests. 9 | * 10 | *

This implementation creates new arrays in the divide phase, so the additional space complexity 11 | * is O(n log n). 12 | * 13 | * @author Sven Woltmann 14 | */ 15 | public class MergeSort3 implements SortAlgorithm { 16 | 17 | // Otherwise we run out of heap 18 | private static final int MAX_INPUT_SIZE = 1 << 28; 19 | 20 | @Override 21 | public void sort(int[] elements) { 22 | mergeSort(elements); 23 | } 24 | 25 | private void mergeSort(int[] elements) { 26 | // End of recursion reached? 27 | int length = elements.length; 28 | if (length == 1) { 29 | return; 30 | } 31 | 32 | int middle = length / 2; 33 | 34 | // Create left + right arrays 35 | int[] left = Arrays.copyOfRange(elements, 0, middle); 36 | int[] right = Arrays.copyOfRange(elements, middle, length); 37 | 38 | // Merge sort them 39 | mergeSort(left); 40 | mergeSort(right); 41 | merge(elements, left, right); 42 | } 43 | 44 | void merge(int[] target, int[] leftArray, int[] rightArray) { 45 | int leftLen = leftArray.length; 46 | int rightLen = rightArray.length; 47 | 48 | int targetPos = 0; 49 | int leftPos = 0; 50 | int rightPos = 0; 51 | 52 | // As long as both lists contain elements... 53 | while (leftPos < leftLen && rightPos < rightLen) { 54 | // Which one is smaller? 55 | int leftValue = leftArray[leftPos]; 56 | int rightValue = rightArray[rightPos]; 57 | if (leftValue <= rightValue) { 58 | target[targetPos++] = leftValue; 59 | leftPos++; 60 | } else { 61 | target[targetPos++] = rightValue; 62 | rightPos++; 63 | } 64 | } 65 | // Copying the rest 66 | while (leftPos < leftLen) { 67 | target[targetPos++] = leftArray[leftPos++]; 68 | } 69 | while (rightPos < rightLen) { 70 | target[targetPos++] = rightArray[rightPos++]; 71 | } 72 | } 73 | 74 | @Override 75 | public void sortWithCounters(int[] elements, Counters counters) { 76 | mergeSortWithCounters(elements, counters); 77 | } 78 | 79 | private void mergeSortWithCounters(int[] elements, Counters counters) { 80 | // End of recursion reached? 81 | int length = elements.length; 82 | if (length == 1) { 83 | return; 84 | } 85 | 86 | int middle = length / 2; 87 | 88 | // Create left + right arrays 89 | int[] left = Arrays.copyOfRange(elements, 0, middle); 90 | int[] right = Arrays.copyOfRange(elements, middle, length); 91 | counters.addReadsAndWrites(length); 92 | 93 | // Merge sort them 94 | mergeSortWithCounters(left, counters); 95 | mergeSortWithCounters(right, counters); 96 | mergeWithCounters(elements, left, right, counters); 97 | } 98 | 99 | void mergeWithCounters(int[] target, int[] leftArray, int[] rightArray, Counters counters) { 100 | int leftLen = leftArray.length; 101 | int rightLen = rightArray.length; 102 | 103 | int targetPos = 0; 104 | int leftPos = 0; 105 | int rightPos = 0; 106 | 107 | // As long as both lists contain elements... 108 | while (leftPos < leftLen && rightPos < rightLen) { 109 | counters.incIterations(); 110 | 111 | // Which one is smaller? 112 | counters.addReads(2); 113 | int leftValue = leftArray[leftPos]; 114 | int rightValue = rightArray[rightPos]; 115 | 116 | counters.incComparisons(); 117 | counters.incWrites(); 118 | if (leftValue <= rightValue) { 119 | target[targetPos++] = leftValue; 120 | leftPos++; 121 | } else { 122 | target[targetPos++] = rightValue; 123 | rightPos++; 124 | } 125 | } 126 | // Copying the rest 127 | while (leftPos < leftLen) { 128 | counters.incIterations(); 129 | target[targetPos++] = leftArray[leftPos++]; 130 | counters.incReadsAndWrites(); 131 | } 132 | while (rightPos < rightLen) { 133 | counters.incIterations(); 134 | target[targetPos++] = rightArray[rightPos++]; 135 | counters.incReadsAndWrites(); 136 | } 137 | } 138 | 139 | @Override 140 | public boolean isSuitableForInputSize(int size) { 141 | return size <= MAX_INPUT_SIZE; 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/sort/method/quicksort/QuicksortSimple.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.quicksort; 2 | 3 | import eu.happycoders.sort.method.Counters; 4 | import eu.happycoders.sort.method.PartitioningAlgorithm; 5 | import eu.happycoders.sort.method.SortAlgorithm; 6 | import eu.happycoders.sort.utils.ArrayUtils; 7 | 8 | /** 9 | * Simple Quicksort implementation (pivot element is always the rightmost element). 10 | * 11 | * @author Sven Woltmann 12 | */ 13 | public class QuicksortSimple implements SortAlgorithm, PartitioningAlgorithm { 14 | 15 | @Override 16 | public void sort(int[] elements) { 17 | quicksort(elements, 0, elements.length - 1); 18 | } 19 | 20 | @Override 21 | public boolean isSuitableForSortedInput(int size) { 22 | return size <= 2 << 12; 23 | } 24 | 25 | private void quicksort(int[] elements, int left, int right) { 26 | // End of recursion reached? 27 | if (left >= right) { 28 | return; 29 | } 30 | 31 | int pivotPos = partition(elements, left, right); 32 | quicksort(elements, left, pivotPos - 1); 33 | quicksort(elements, pivotPos + 1, right); 34 | } 35 | 36 | @Override 37 | public int partition(int[] elements, int left, int right) { 38 | int pivot = elements[right]; 39 | 40 | int i = left; 41 | int j = right - 1; 42 | while (i < j) { 43 | // Find the first element >= pivot 44 | while (elements[i] < pivot) { 45 | i++; 46 | } 47 | 48 | // Find the last element < pivot 49 | while (j > left && elements[j] >= pivot) { 50 | j--; 51 | } 52 | 53 | // If the greater element is left of the lesser element, switch them 54 | if (i < j) { 55 | ArrayUtils.swap(elements, i, j); 56 | i++; 57 | j--; 58 | } 59 | } 60 | 61 | // i == j means we haven't checked this index yet. 62 | // Move i right if necessary so that i marks the start of the right array. 63 | if (i == j && elements[i] < pivot) { 64 | i++; 65 | } 66 | 67 | // Move pivot element to its final position 68 | if (elements[i] != pivot) { 69 | ArrayUtils.swap(elements, i, right); 70 | } 71 | return i; 72 | } 73 | 74 | @Override 75 | public void sortWithCounters(int[] elements, Counters counters) { 76 | quicksortWithCounters(elements, 0, elements.length - 1, counters); 77 | } 78 | 79 | private void quicksortWithCounters(int[] elements, int left, int right, Counters counters) { 80 | // End of recursion reached? 81 | if (left >= right) { 82 | return; 83 | } 84 | 85 | int pivotPos = partitionWithCounters(elements, left, right, counters); 86 | quicksortWithCounters(elements, left, pivotPos - 1, counters); 87 | quicksortWithCounters(elements, pivotPos + 1, right, counters); 88 | } 89 | 90 | @Override 91 | public int partitionWithCounters(int[] elements, int left, int right, Counters counters) { 92 | int pivot = elements[right]; 93 | 94 | int i = left; 95 | int j = right - 1; 96 | while (i < j) { 97 | counters.incIterations(); 98 | 99 | // Find the first element >= pivot 100 | while (true) { 101 | counters.incComparisons(); 102 | counters.incReads(); 103 | if (elements[i] < pivot) { 104 | i++; 105 | } else { 106 | break; 107 | } 108 | } 109 | 110 | // Find the last element < pivot 111 | while (true) { 112 | counters.incComparisons(); 113 | counters.incReads(); 114 | if (j > left && elements[j] >= pivot) { 115 | j--; 116 | } else { 117 | break; 118 | } 119 | } 120 | 121 | // If the greater element is left of the lesser element, switch them 122 | if (i < j) { 123 | ArrayUtils.swap(elements, i, j); 124 | counters.addReadsAndWrites(2); 125 | i++; 126 | j--; 127 | } 128 | } 129 | 130 | // i == j means we haven't checked this index yet. 131 | // Move i right if necessary so that i marks the start of the right array. 132 | if (i == j) { 133 | counters.incReads(); 134 | counters.incComparisons(); 135 | if (elements[i] < pivot) { 136 | i++; 137 | } 138 | } 139 | 140 | // Move pivot element to its final position 141 | counters.incReads(); 142 | counters.incComparisons(); 143 | if (elements[i] != pivot) { 144 | ArrayUtils.swap(elements, i, right); 145 | counters.addReadsAndWrites(2); 146 | } 147 | return i; 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/sort/method/heapsort/BottomUpHeapsort.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.heapsort; 2 | 3 | import eu.happycoders.sort.method.Counters; 4 | 5 | /** 6 | * Bottom-up heapsort implementation. 7 | * 8 | * @author Sven Woltmann 9 | */ 10 | public class BottomUpHeapsort extends Heapsort { 11 | 12 | /** 13 | * "Fixes" a max heap starting at the given parent position. 14 | * 15 | * @param heap the heap to be fixed 16 | * @param length the number of elements in the array that belong to the heap 17 | * @param rootPos the parent position 18 | */ 19 | @Override 20 | void heapify(int[] heap, int length, int rootPos) { 21 | int leafPos = findLeaf(heap, length, rootPos); 22 | int nodePos = findTargetNodeBottomUp(heap, rootPos, leafPos); 23 | 24 | if (rootPos == nodePos) { 25 | return; 26 | } 27 | 28 | // Move all elements starting at nodePos to parent, move root to nodePos 29 | int nodeValue = heap[nodePos]; 30 | heap[nodePos] = heap[rootPos]; 31 | 32 | while (nodePos > rootPos) { 33 | int parentPos = getParentPos(nodePos); 34 | int parentValue = heap[parentPos]; 35 | heap[parentPos] = nodeValue; 36 | nodePos = getParentPos(nodePos); 37 | nodeValue = parentValue; 38 | } 39 | } 40 | 41 | int findLeaf(int[] heap, int length, int rootPos) { 42 | int pos = rootPos; 43 | int leftChildPos = pos * 2 + 1; 44 | int rightChildPos = pos * 2 + 2; 45 | 46 | // Two child exist? 47 | while (rightChildPos < length) { 48 | if (heap[rightChildPos] > heap[leftChildPos]) { 49 | pos = rightChildPos; 50 | } else { 51 | pos = leftChildPos; 52 | } 53 | leftChildPos = pos * 2 + 1; 54 | rightChildPos = pos * 2 + 2; 55 | } 56 | 57 | // One child exist? 58 | if (leftChildPos < length) { 59 | pos = leftChildPos; 60 | } 61 | 62 | return pos; 63 | } 64 | 65 | int findTargetNodeBottomUp(int[] heap, int rootPos, int leafPos) { 66 | int parent = heap[rootPos]; 67 | while (leafPos != rootPos && heap[leafPos] < parent) { 68 | leafPos = getParentPos(leafPos); 69 | } 70 | return leafPos; 71 | } 72 | 73 | int getParentPos(int pos) { 74 | return (pos - 1) / 2; 75 | } 76 | 77 | @Override 78 | void heapifyWithCounters(int[] heap, int length, int rootPos, Counters counters) { 79 | int leafPos = findLeafWithCounters(heap, length, rootPos, counters); 80 | int nodePos = findTargetNodeBottomUpWithCounters(heap, rootPos, leafPos, counters); 81 | 82 | if (rootPos == nodePos) { 83 | return; 84 | } 85 | 86 | // Move all elements starting at nodePos to parent, move root to nodePos 87 | counters.incReads(); 88 | int nodeValue = heap[nodePos]; 89 | counters.incReadsAndWrites(); 90 | heap[nodePos] = heap[rootPos]; 91 | 92 | while (nodePos > rootPos) { 93 | counters.incIterations(); 94 | int parentPos = getParentPos(nodePos); 95 | counters.incReadsAndWrites(); 96 | int parentValue = heap[parentPos]; 97 | heap[parentPos] = nodeValue; 98 | nodePos = parentPos; 99 | nodeValue = parentValue; 100 | } 101 | } 102 | 103 | private int findLeafWithCounters(int[] heap, int length, int rootPos, Counters counters) { 104 | int pos = rootPos; 105 | int leftChildPos = pos * 2 + 1; 106 | int rightChildPos = pos * 2 + 2; 107 | 108 | while (rightChildPos < length) { 109 | counters.incIterations(); 110 | counters.addReads(2); 111 | counters.incComparisons(); 112 | if (heap[rightChildPos] > heap[leftChildPos]) { 113 | pos = rightChildPos; 114 | } else { 115 | pos = leftChildPos; 116 | } 117 | leftChildPos = pos * 2 + 1; 118 | rightChildPos = pos * 2 + 2; 119 | } 120 | 121 | if (leftChildPos < length) { 122 | pos = leftChildPos; 123 | } 124 | 125 | return pos; 126 | } 127 | 128 | private int findTargetNodeBottomUpWithCounters( 129 | int[] heap, int rootPos, int leafPos, Counters counters) { 130 | counters.incReads(); 131 | int parentValue = heap[rootPos]; 132 | int nodePos = leafPos; 133 | while (nodePos != rootPos && nodeSmallerThanParent(heap[nodePos], parentValue, counters)) { 134 | counters.incIterations(); 135 | nodePos = getParentPos(nodePos); 136 | } 137 | return nodePos; 138 | } 139 | 140 | private boolean nodeSmallerThanParent(int nodeValue, int parentValue, Counters counters) { 141 | counters.incReads(); 142 | counters.incComparisons(); 143 | return nodeValue < parentValue; 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/main/java/eu/happycoders/sort/method/mergesort/MergeSort2.java: -------------------------------------------------------------------------------- 1 | package eu.happycoders.sort.method.mergesort; 2 | 3 | import eu.happycoders.sort.method.Counters; 4 | import eu.happycoders.sort.method.SortAlgorithm; 5 | 6 | /** 7 | * Merge sort implementation for performance tests. 8 | * 9 | *

This implementation divides directly on the input array and creates new arrays only in the 10 | * merge phase, copying them immediately back into the input array after merging. 11 | * 12 | *

Additional space complexity is O(n). 13 | * 14 | * @author Sven Woltmann 15 | */ 16 | public class MergeSort2 implements SortAlgorithm { 17 | 18 | // Otherwise we run out of heap 19 | private static final int MAX_INPUT_SIZE = 1 << 28; 20 | 21 | @Override 22 | public void sort(int[] elements) { 23 | mergeSort(elements, 0, elements.length - 1); 24 | } 25 | 26 | private void mergeSort(int[] elements, int left, int right) { 27 | // End of recursion reached? 28 | if (left == right) { 29 | return; 30 | } 31 | 32 | int middle = left + (right - left) / 2; 33 | mergeSort(elements, left, middle); 34 | mergeSort(elements, middle + 1, right); 35 | merge(elements, left, middle, right); 36 | } 37 | 38 | void merge(int[] elements, int leftStart, int leftEnd, int rightEnd) { 39 | int leftPos = leftStart; 40 | int rightPos = leftEnd + 1; 41 | 42 | int length = rightEnd + 1 - leftPos; 43 | int[] target = new int[length]; 44 | int targetPos = 0; 45 | 46 | // As long as both lists contain elements... 47 | while (leftPos <= leftEnd && rightPos <= rightEnd) { 48 | // Which one is smaller? 49 | int leftValue = elements[leftPos]; 50 | int rightValue = elements[rightPos]; 51 | if (leftValue <= rightValue) { 52 | target[targetPos++] = leftValue; 53 | leftPos++; 54 | } else { 55 | target[targetPos++] = rightValue; 56 | rightPos++; 57 | } 58 | } 59 | // Copying the rest 60 | while (leftPos <= leftEnd) { 61 | target[targetPos++] = elements[leftPos++]; 62 | } 63 | while (rightPos <= rightEnd) { 64 | target[targetPos++] = elements[rightPos++]; 65 | } 66 | // Write temporary array back into array to be sorted 67 | System.arraycopy(target, 0, elements, leftStart, length); 68 | } 69 | 70 | @Override 71 | public void sortWithCounters(int[] elements, Counters counters) { 72 | mergeSortWithCounters(elements, 0, elements.length - 1, counters); 73 | } 74 | 75 | private void mergeSortWithCounters(int[] elements, int left, int right, Counters counters) { 76 | // End of recursion reached? 77 | if (left == right) { 78 | return; 79 | } 80 | 81 | int middle = left + (right - left) / 2; 82 | mergeSortWithCounters(elements, left, middle, counters); 83 | mergeSortWithCounters(elements, middle + 1, right, counters); 84 | mergeWithCounters(elements, left, middle, right, counters); 85 | } 86 | 87 | void mergeWithCounters( 88 | int[] elements, int leftStart, int leftEnd, int rightEnd, Counters counters) { 89 | int leftPos = leftStart; 90 | int rightPos = leftEnd + 1; 91 | 92 | int length = rightEnd + 1 - leftPos; 93 | int[] target = new int[length]; 94 | int targetPos = 0; 95 | 96 | // As long as both lists contain elements... 97 | while (leftPos <= leftEnd && rightPos <= rightEnd) { 98 | counters.incIterations(); 99 | 100 | // Which one is smaller? 101 | counters.addReads(2); 102 | int leftValue = elements[leftPos]; 103 | int rightValue = elements[rightPos]; 104 | 105 | counters.incComparisons(); 106 | counters.incWrites(); 107 | if (leftValue <= rightValue) { 108 | target[targetPos++] = leftValue; 109 | leftPos++; 110 | } else { 111 | target[targetPos++] = rightValue; 112 | rightPos++; 113 | } 114 | } 115 | // Copying the rest 116 | while (leftPos <= leftEnd) { 117 | counters.incIterations(); 118 | target[targetPos++] = elements[leftPos++]; 119 | counters.incReadsAndWrites(); 120 | } 121 | while (rightPos <= rightEnd) { 122 | counters.incIterations(); 123 | target[targetPos++] = elements[rightPos++]; 124 | counters.incReadsAndWrites(); 125 | } 126 | // Write temporary array back into array to be sorted 127 | System.arraycopy(target, 0, elements, leftStart, length); 128 | counters.addReadsAndWrites(length); 129 | } 130 | 131 | @Override 132 | public boolean isSuitableForInputSize(int size) { 133 | return size <= MAX_INPUT_SIZE; 134 | } 135 | } 136 | --------------------------------------------------------------------------------