From 7692e8f47dda31167ef27fc478c8617a67418d3c Mon Sep 17 00:00:00 2001 From: Narek Karapetian Date: Fri, 25 Nov 2022 09:03:04 -0800 Subject: [PATCH] Heap Sort: Simplify (#3777) * bug fix for CircularBuffer + refactoring + add unit tests * change Insertion sort to classical implementation + add isSorted function to SortUtils + add SortUtilsRandomGenerator for generating random values and arrays * little fix * simplify heap sort * Update src/main/java/com/thealgorithms/sorts/HeapSort.java * Update src/main/java/com/thealgorithms/sorts/HeapSort.java Co-authored-by: Debasish Biswas --- .../com/thealgorithms/sorts/HeapSort.java | 154 +++++------------- .../com/thealgorithms/sorts/HeapSortTest.java | 104 +++++++++--- 2 files changed, 124 insertions(+), 134 deletions(-) diff --git a/src/main/java/com/thealgorithms/sorts/HeapSort.java b/src/main/java/com/thealgorithms/sorts/HeapSort.java index 1a96eaa3..eec705ba 100644 --- a/src/main/java/com/thealgorithms/sorts/HeapSort.java +++ b/src/main/java/com/thealgorithms/sorts/HeapSort.java @@ -1,126 +1,56 @@ package com.thealgorithms.sorts; -import static com.thealgorithms.sorts.SortUtils.*; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - /** - * Heap Sort Algorithm Implements MinHeap + * Heap Sort Algorithm Implementation * - * @author Podshivalov Nikita (https://github.com/nikitap492) + * @see Heap Sort Algorithm */ public class HeapSort implements SortAlgorithm { - private static class Heap> { - - /** - * Array to store heap - */ - private T[] heap; - - /** - * Constructor - * - * @param heap array of unordered integers - */ - public Heap(T[] heap) { - this.heap = heap; - } - - /** - * Heapifies subtree from top as root to last as last child - * - * @param rootIndex index of root - * @param lastChild index of last child - */ - private void heapSubtree(int rootIndex, int lastChild) { - int leftIndex = rootIndex * 2 + 1; - int rightIndex = rootIndex * 2 + 2; - T root = heap[rootIndex]; - if (rightIndex <= lastChild) { // if has right and left children - T left = heap[leftIndex]; - T right = heap[rightIndex]; - if (less(left, right) && less(left, root)) { - swap(heap, leftIndex, rootIndex); - heapSubtree(leftIndex, lastChild); - } else if (less(right, root)) { - swap(heap, rightIndex, rootIndex); - heapSubtree(rightIndex, lastChild); - } - } else if (leftIndex <= lastChild) { // if no right child, but has left child - T left = heap[leftIndex]; - if (less(left, root)) { - swap(heap, leftIndex, rootIndex); - heapSubtree(leftIndex, lastChild); - } - } - } - - /** - * Makes heap with root as root - * - * @param root index of root of heap - */ - private void makeMinHeap(int root) { - int leftIndex = root * 2 + 1; - int rightIndex = root * 2 + 2; - boolean hasLeftChild = leftIndex < heap.length; - boolean hasRightChild = rightIndex < heap.length; - if (hasRightChild) { // if has left and right - makeMinHeap(leftIndex); - makeMinHeap(rightIndex); - heapSubtree(root, heap.length - 1); - } else if (hasLeftChild) { - heapSubtree(root, heap.length - 1); - } - } - - /** - * Gets the root of heap - * - * @return root of heap - */ - private T getRoot(int size) { - swap(heap, 0, size); - heapSubtree(0, size - 1); - return heap[size]; // return old root - } - } - + /** + * For simplicity, we are considering the heap root index as 1 instead of 0. + * It simplifies future calculations. Because of that we are decreasing the + * provided indexes by 1 in {@link #swap(Object[], int, int)} and + * {@link #less(Comparable[], int, int)} functions. + */ @Override public > T[] sort(T[] unsorted) { - return sort(Arrays.asList(unsorted)).toArray(unsorted); - } - - @Override - public > List sort(List unsorted) { - int size = unsorted.size(); - - @SuppressWarnings("unchecked") - Heap heap = new Heap<>( - unsorted.toArray((T[]) new Comparable[unsorted.size()]) - ); - - heap.makeMinHeap(0); // make min heap using index 0 as root. - List sorted = new ArrayList<>(size); - while (size > 0) { - T min = heap.getRoot(--size); - sorted.add(min); + int n = unsorted.length; + heapify(unsorted, n); + while (n > 1) { + swap(unsorted, 1, n--); + siftDown(unsorted, 1, n); } - - return sorted; + return unsorted; } - /** - * Main method - * - * @param args the command line arguments - */ - public static void main(String[] args) { - Integer[] heap = { 4, 23, 6, 78, 1, 54, 231, 9, 12 }; - HeapSort heapSort = new HeapSort(); - print(heapSort.sort(heap)); + private static > void heapify(T[] unsorted, int n) { + for (int k = n / 2; k >= 1; k--) { + siftDown(unsorted, k, n); + } + } + + private static > void siftDown(T[] unsorted, int k, int n) { + while (2 * k <= n) { + int j = 2 * k; + if (j < n && less(unsorted, j, j + 1)) { + j++; + } + if (!less(unsorted, k, j)) { + break; + } + swap(unsorted, k, j); + k = j; + } + } + + private static void swap(T[] array, int idx, int idy) { + T swap = array[idx - 1]; + array[idx - 1] = array[idy - 1]; + array[idy - 1] = swap; + } + + private static > boolean less(T[] array, int idx, int idy) { + return array[idx - 1].compareTo(array[idy - 1]) < 0; } } diff --git a/src/test/java/com/thealgorithms/sorts/HeapSortTest.java b/src/test/java/com/thealgorithms/sorts/HeapSortTest.java index 7b34c59f..71a5ec3a 100644 --- a/src/test/java/com/thealgorithms/sorts/HeapSortTest.java +++ b/src/test/java/com/thealgorithms/sorts/HeapSortTest.java @@ -1,35 +1,95 @@ package com.thealgorithms.sorts; import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class HeapSortTest { - - private HeapSort heapSort = new HeapSort(); - - @Test - void testHeapSortCase1() { - Integer[] array = { 49, 4, 36, 9, 144, 1 }; - Integer[] sorted = heapSort.sort(array); - Integer[] expected = { 1, 4, 9, 36, 49, 144 }; - assertArrayEquals(expected, sorted); + private HeapSort heapSort; + + @BeforeEach + void setUp() { + heapSort = new HeapSort(); } - - @Test - void testHeapSortCase2() { - Integer[] array = { }; + + @Test + void shouldAcceptWhenEmptyArrayIsPassed() { + Integer[] array = new Integer[]{}; + Integer[] expected = new Integer[]{}; + Integer[] sorted = heapSort.sort(array); - Integer[] expected = { }; - assertArrayEquals(expected, sorted); - } - - @Test - void testHeapSortCase3 () { - Integer[] array = { -3, 5, 3, 4, 3, 7, 40, -20, 30, 0 }; - Integer[] sorted = heapSort.sort(array); - Integer[] expected = { -20, -3, 0, 3, 3, 4, 5, 7, 30, 40 }; + assertArrayEquals(expected, sorted); } + @Test + void shouldAcceptWhenSingleValuedArrayIsPassed() { + Integer[] array = new Integer[]{2}; + Integer[] expected = new Integer[]{2}; + + Integer[] sorted = heapSort.sort(array); + + assertArrayEquals(expected, sorted); + } + + @Test + void shouldAcceptWhenArrayWithAllPositiveValuesIsPassed() { + Integer[] array = new Integer[]{60, 7, 55, 9, 999, 3}; + Integer[] expected = new Integer[]{3, 7, 9, 55, 60, 999}; + + Integer[] sorted = heapSort.sort(array); + + assertArrayEquals(expected, sorted); + } + + @Test + void shouldAcceptWhenArrayWithAllNegativeValuesIsPassed() { + Integer[] array = new Integer[]{-60, -7, -55, -9, -999, -3}; + Integer[] expected = new Integer[]{-999, -60, -55, -9, -7, -3}; + + Integer[] sorted = heapSort.sort(array); + + assertArrayEquals(expected, sorted); + } + + @Test + void shouldAcceptWhenArrayWithRealNumberValuesIsPassed() { + Integer[] array = new Integer[]{60, -7, 55, 9, -999, -3}; + Integer[] expected = new Integer[]{-999, -7, -3, 9, 55, 60}; + + Integer[] sorted = heapSort.sort(array); + + assertArrayEquals(expected, sorted); + } + + @Test + void shouldAcceptWhenArrayWithDuplicateValueIsPassed() { + Integer[] array = new Integer[]{60, 7, 55, 55, 999, 3}; + Integer[] expected = new Integer[]{3, 7, 55, 55, 60, 999}; + + Integer[] sorted = heapSort.sort(array); + + assertArrayEquals(expected, sorted); + } + + @Test + void shouldAcceptWhenStringValueArrayIsPassed() { + String[] array = {"z", "a", "x", "b", "y"}; + String[] expected = {"a", "b", "x", "y", "z"}; + + String[] sorted = heapSort.sort(array); + + assertArrayEquals(expected, sorted); + } + + @Test + void shouldAcceptWhenRandomArrayIsPassed() { + int randomSize = SortUtilsRandomGenerator.generateInt(10_000); + Double[] array = SortUtilsRandomGenerator.generateArray(randomSize); + Double[] sorted = heapSort.sort(array); + assertTrue(SortUtils.isSorted(sorted)); + } + }