From 2a9149115879d8ba8bd7f4b1dc552e85c1f26dae Mon Sep 17 00:00:00 2001 From: narek_karapetian Date: Thu, 6 Oct 2022 11:13:56 +0400 Subject: [PATCH 1/6] bug fix for CircularBuffer + refactoring + add unit tests --- .../buffers/CircularBuffer.java | 147 +++++------------- .../buffers/CircularBufferTest.java | 127 +++++++++++++++ 2 files changed, 165 insertions(+), 109 deletions(-) create mode 100644 src/test/java/com/thealgorithms/datastructures/buffers/CircularBufferTest.java diff --git a/src/main/java/com/thealgorithms/datastructures/buffers/CircularBuffer.java b/src/main/java/com/thealgorithms/datastructures/buffers/CircularBuffer.java index 6a6dcd8fe7e2..96c72fc04d6a 100644 --- a/src/main/java/com/thealgorithms/datastructures/buffers/CircularBuffer.java +++ b/src/main/java/com/thealgorithms/datastructures/buffers/CircularBuffer.java @@ -1,132 +1,61 @@ package com.thealgorithms.datastructures.buffers; -import java.util.Random; import java.util.concurrent.atomic.AtomicInteger; -public class CircularBuffer { +public class CircularBuffer { + private final Item[] buffer; + private final CircularPointer putPointer; + private final CircularPointer getPointer; + private final AtomicInteger size = new AtomicInteger(0); - private char[] _buffer; - public final int _buffer_size; - private int _write_index = 0; - private int _read_index = 0; - private AtomicInteger _readable_data = new AtomicInteger(0); - - public CircularBuffer(int buffer_size) { - if (!IsPowerOfTwo(buffer_size)) { - throw new IllegalArgumentException(); - } - this._buffer_size = buffer_size; - _buffer = new char[buffer_size]; + public CircularBuffer(int size) { + //noinspection unchecked + this.buffer = (Item[]) new Object[size]; + this.putPointer = new CircularPointer(0, size); + this.getPointer = new CircularPointer(0, size); } - private boolean IsPowerOfTwo(int i) { - return (i & (i - 1)) == 0; + public boolean isEmpty() { + return size.get() == 0; } - private int getTrueIndex(int i) { - return i % _buffer_size; + public boolean isFull() { + return size.get() == buffer.length; } - public Character readOutChar() { - Character result = null; - - // if we have data to read - if (_readable_data.get() > 0) { - result = Character.valueOf(_buffer[getTrueIndex(_read_index)]); - _readable_data.decrementAndGet(); - _read_index++; - } + public Item get() { + if (isEmpty()) + return null; - return result; + Item item = buffer[getPointer.getAndIncrement()]; + size.decrementAndGet(); + return item; } - public boolean writeToCharBuffer(char c) { - boolean result = false; - - // if we can write to the buffer - if (_readable_data.get() < _buffer_size) { - // write to buffer - _buffer[getTrueIndex(_write_index)] = c; - _readable_data.incrementAndGet(); - _write_index++; - result = true; - } + public boolean put(Item item) { + if (isFull()) + return false; - return result; + buffer[putPointer.getAndIncrement()] = item; + size.incrementAndGet(); + return true; } - private static class TestWriteWorker implements Runnable { + private static class CircularPointer { + private int pointer; + private final int max; - String _alphabet = "abcdefghijklmnopqrstuvwxyz0123456789"; - Random _random = new Random(); - CircularBuffer _buffer; - - public TestWriteWorker(CircularBuffer cb) { - this._buffer = cb; - } - - private char getRandomChar() { - return _alphabet.charAt(_random.nextInt(_alphabet.length())); + public CircularPointer(int pointer, int max) { + this.pointer = pointer; + this.max = max; } - public void run() { - while (!Thread.interrupted()) { - if (!_buffer.writeToCharBuffer(getRandomChar())) { - Thread.yield(); - try { - Thread.sleep(10); - } catch (InterruptedException e) { - return; - } - } - } + public int getAndIncrement() { + if (pointer == max) + pointer = 0; + int tmp = pointer; + pointer++; + return tmp; } } - - private static class TestReadWorker implements Runnable { - - CircularBuffer _buffer; - - public TestReadWorker(CircularBuffer cb) { - this._buffer = cb; - } - - @Override - public void run() { - System.out.println("Printing Buffer:"); - while (!Thread.interrupted()) { - Character c = _buffer.readOutChar(); - if (c != null) { - System.out.print(c.charValue()); - } else { - Thread.yield(); - try { - Thread.sleep(10); - } catch (InterruptedException e) { - System.out.println(); - return; - } - } - } - } - } - - public static void main(String[] args) throws InterruptedException { - int buffer_size = 1024; - // create circular buffer - CircularBuffer cb = new CircularBuffer(buffer_size); - - // create threads that read and write the buffer. - Thread write_thread = new Thread(new TestWriteWorker(cb)); - Thread read_thread = new Thread(new TestReadWorker(cb)); - read_thread.start(); - write_thread.start(); - - // wait some amount of time - Thread.sleep(10000); - - // interrupt threads and exit - write_thread.interrupt(); - read_thread.interrupt(); - } } diff --git a/src/test/java/com/thealgorithms/datastructures/buffers/CircularBufferTest.java b/src/test/java/com/thealgorithms/datastructures/buffers/CircularBufferTest.java new file mode 100644 index 000000000000..70fb6608e481 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/buffers/CircularBufferTest.java @@ -0,0 +1,127 @@ +package com.thealgorithms.datastructures.buffers; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.RepeatedTest; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicIntegerArray; + +import static org.junit.jupiter.api.Assertions.*; + +class CircularBufferTest { + private static final int BUFFER_SIZE = 10; + private CircularBuffer buffer; + + @BeforeEach + void setUp() { + buffer = new CircularBuffer<>(BUFFER_SIZE); + } + + @Test + void isEmpty() { + assertTrue(buffer.isEmpty()); + buffer.put(generateInt()); + assertFalse(buffer.isEmpty()); + } + + @Test + void isFull() { + assertFalse(buffer.isFull()); + buffer.put(generateInt()); + assertFalse(buffer.isFull()); + + for (int i = 1; i < BUFFER_SIZE; i++) + buffer.put(generateInt()); + assertTrue(buffer.isFull()); + } + + @Test + void get() { + assertNull(buffer.get()); + for (int i = 0; i < 100; i++) + buffer.put(i); + for (int i = 0; i < BUFFER_SIZE; i++) + assertEquals(i, buffer.get()); + assertNull(buffer.get()); + } + + @Test + void put() { + for (int i = 0; i < BUFFER_SIZE; i++) + assertTrue(buffer.put(generateInt())); + assertFalse(buffer.put(generateInt())); + } + + @RepeatedTest(1000) + void concurrentTest() throws InterruptedException { + final int numberOfThreadsForProducers = 3; + final int numberOfThreadsForConsumers = 2; + final int numberOfItems = 300; + final CountDownLatch producerCountDownLatch = new CountDownLatch(numberOfItems); + final CountDownLatch consumerCountDownLatch = new CountDownLatch(numberOfItems); + final AtomicIntegerArray resultAtomicArray = new AtomicIntegerArray(numberOfItems); + + // We are running 2 ExecutorService simultaneously 1 - producer, 2 - consumer + // Run producer threads to populate buffer. + ExecutorService putExecutors = Executors.newFixedThreadPool(numberOfThreadsForProducers); + putExecutors.execute(() -> { + while (producerCountDownLatch.getCount() > 0) { + int count = (int) producerCountDownLatch.getCount(); + boolean put = buffer.put(count); + while (!put) put = buffer.put(count); + producerCountDownLatch.countDown(); + } + }); + + // Run consumer threads to retrieve the data from buffer. + ExecutorService getExecutors = Executors.newFixedThreadPool(numberOfThreadsForConsumers); + getExecutors.execute(() -> { + while (consumerCountDownLatch.getCount() > 0) { + int count = (int) consumerCountDownLatch.getCount(); + Integer item = buffer.get(); + while (item == null) item = buffer.get(); + resultAtomicArray.set(count - 1, item); + consumerCountDownLatch.countDown(); + } + }); + + producerCountDownLatch.await(); + consumerCountDownLatch.await(); + putExecutors.shutdown(); + getExecutors.shutdown(); + shutDownExecutorSafely(putExecutors); + shutDownExecutorSafely(getExecutors); + + List resultArray = getSortedListFrom(resultAtomicArray); + for (int i = 0; i < numberOfItems; i++) { + int expectedItem = i + 1; + assertEquals(expectedItem, resultArray.get(i)); + } + } + + private int generateInt() { + return ThreadLocalRandom.current().nextInt(0, 100); + } + + private void shutDownExecutorSafely(ExecutorService executorService) { + try { + if (!executorService.awaitTermination(1_000, TimeUnit.MILLISECONDS)) + executorService.shutdownNow(); + } catch (InterruptedException e) { + executorService.shutdownNow(); + } + } + + public List getSortedListFrom(AtomicIntegerArray atomicArray) { + int length = atomicArray.length(); + ArrayList result = new ArrayList<>(length); + for (int i = 0; i < length; i++) + result.add(atomicArray.get(i)); + result.sort(Comparator.comparingInt(o -> o)); + return result; + } +} From aa1f18aff125b66b6713cdde92a47fc3a07bc30d Mon Sep 17 00:00:00 2001 From: narek_karapetian Date: Fri, 21 Oct 2022 22:29:24 +0400 Subject: [PATCH 2/6] change Insertion sort to classical implementation + add isSorted function to SortUtils + add SortUtilsRandomGenerator for generating random values and arrays --- .../thealgorithms/sorts/InsertionSort.java | 85 +++++++++---- .../com/thealgorithms/sorts/SortUtils.java | 13 ++ .../sorts/SortUtilsRandomGenerator.java | 38 ++++++ .../sorts/InsertionSortTest.java | 114 ++++++++++++++++++ .../sorts/SortUtilsRandomGeneratorTest.java | 31 +++++ .../thealgorithms/sorts/SortUtilsTest.java | 44 +++++++ 6 files changed, 304 insertions(+), 21 deletions(-) create mode 100644 src/main/java/com/thealgorithms/sorts/SortUtilsRandomGenerator.java create mode 100644 src/test/java/com/thealgorithms/sorts/InsertionSortTest.java create mode 100644 src/test/java/com/thealgorithms/sorts/SortUtilsRandomGeneratorTest.java create mode 100644 src/test/java/com/thealgorithms/sorts/SortUtilsTest.java diff --git a/src/main/java/com/thealgorithms/sorts/InsertionSort.java b/src/main/java/com/thealgorithms/sorts/InsertionSort.java index 9fec4d408fb6..594379a516f5 100644 --- a/src/main/java/com/thealgorithms/sorts/InsertionSort.java +++ b/src/main/java/com/thealgorithms/sorts/InsertionSort.java @@ -1,7 +1,8 @@ package com.thealgorithms.sorts; -import static com.thealgorithms.sorts.SortUtils.less; -import static com.thealgorithms.sorts.SortUtils.print; +import java.util.function.Function; + +import static com.thealgorithms.sorts.SortUtils.*; class InsertionSort implements SortAlgorithm { @@ -9,21 +10,51 @@ class InsertionSort implements SortAlgorithm { * Generic insertion sort algorithm in increasing order. * * @param array the array to be sorted. - * @param the class of array. + * @param the class of array. * @return sorted array. */ @Override public > T[] sort(T[] array) { - for (int i = 1; i < array.length; i++) { - T insertValue = array[i]; - int j; - for (j = i - 1; j >= 0 && less(insertValue, array[j]); j--) { - array[j + 1] = array[j]; - } - if (j != i - 1) { - array[j + 1] = insertValue; + for (int i = 1; i < array.length; i++) + for (int j = i; j > 0 && less(array[j], array[j - 1]); j--) + swap(array, j, j - 1); + return array; + } + + /** + * Sentinel sort is a function which on the first step finds the minimal element in the provided + * array and puts it to the zero position, such a trick gives us an ability to avoid redundant + * comparisons like `j > 0` and swaps (we can move elements on position right, until we find + * the right position for the chosen element) on further step. + * + * @param array the array to be sorted + * @param Generic type which extends Comparable interface. + * @return sorted array + */ + public > T[] sentinelSort(T[] array) { + int minElemIndex = 0; + int n = array.length; + if (n < 1) + return array; + + // put the smallest element to the 0 position as a sentinel, which will allow us to avoid + // redundant comparisons like `j > 0` further + for (int i = 1; i < n; i++) + if (less(array[i], array[minElemIndex])) + minElemIndex = i; + swap(array, 0, minElemIndex); + + for (int i = 2; i < n; i++) { + int j = i; + T currentValue = array[i]; + while (less(currentValue, array[j - 1])) { + array[j] = array[j - 1]; + j--; } + + array[j] = currentValue; } + return array; } @@ -31,15 +62,27 @@ public > T[] sort(T[] array) { * Driver Code */ public static void main(String[] args) { - Integer[] integers = { 4, 23, 6, 78, 1, 54, 231, 9, 12 }; - InsertionSort sort = new InsertionSort(); - sort.sort(integers); - print(integers); - /* [1, 4, 6, 9, 12, 23, 54, 78, 231] */ - - String[] strings = { "c", "a", "e", "b", "d" }; - sort.sort(strings); - print(strings); - /* [a, b, c, d, e] */ + int size = 100_000; + Double[] randomArray = SortUtilsRandomGenerator.generateArray(size); + Double[] copyRandomArray = new Double[size]; + System.arraycopy(randomArray, 0, copyRandomArray, 0, size); + + InsertionSort insertionSort = new InsertionSort(); + double insertionTime = measureApproxExecTime(insertionSort::sort, randomArray); + System.out.printf("Original insertion time: %5.2f sec.\n", insertionTime); + + double insertionSentinelTime = measureApproxExecTime(insertionSort::sentinelSort, copyRandomArray); + System.out.printf("Sentinel insertion time: %5.2f sec.\n", insertionSentinelTime); + + // ~ 1.5 time sentinel sort is faster, then classical Insertion sort implementation. + System.out.printf("Sentinel insertion is %f3.2 time faster than Original insertion sort\n", + insertionTime / insertionSentinelTime); + } + + private static double measureApproxExecTime(Function sortAlgorithm, Double[] randomArray) { + long start = System.currentTimeMillis(); + sortAlgorithm.apply(randomArray); + long end = System.currentTimeMillis(); + return (end - start) / 1000.0; } } diff --git a/src/main/java/com/thealgorithms/sorts/SortUtils.java b/src/main/java/com/thealgorithms/sorts/SortUtils.java index 27774103861c..83b94c9a922f 100644 --- a/src/main/java/com/thealgorithms/sorts/SortUtils.java +++ b/src/main/java/com/thealgorithms/sorts/SortUtils.java @@ -99,4 +99,17 @@ static > void flip(T[] array, int left, int right) { swap(array, left++, right--); } } + + /** + * Function to check if the array is sorted. By default, it will check if the array is sorted in ASC order. + * + * @param array - an array which to check is it sorted or not. + * @return true - if array sorted in ASC order, false otherwise. + */ + static > boolean isSorted(T[] array) { + for (int i = 1; i < array.length; i++) + if (less(array[i], array[i - 1])) + return false; + return true; + } } diff --git a/src/main/java/com/thealgorithms/sorts/SortUtilsRandomGenerator.java b/src/main/java/com/thealgorithms/sorts/SortUtilsRandomGenerator.java new file mode 100644 index 000000000000..aad511c78201 --- /dev/null +++ b/src/main/java/com/thealgorithms/sorts/SortUtilsRandomGenerator.java @@ -0,0 +1,38 @@ +package com.thealgorithms.sorts; + +import java.util.Random; + +public class SortUtilsRandomGenerator { + + private static final Random random; + private static final long seed; + + static { + seed = System.currentTimeMillis(); + random = new Random(seed); + } + + + /** + * Function to generate array of double values, with predefined size. + * + * @param size result array size + * @return array of Double values, randomly generated, each element is between [0, 1) + */ + public static Double[] generateArray(int size) { + Double[] arr = new Double[size]; + for (int i = 0; i < size; i++) + arr[i] = generateDouble(); + return arr; + } + + /** + * Function to generate Double value. + * + * @return Double value [0, 1) + */ + public static Double generateDouble() { + return random.nextDouble(); + } + +} diff --git a/src/test/java/com/thealgorithms/sorts/InsertionSortTest.java b/src/test/java/com/thealgorithms/sorts/InsertionSortTest.java new file mode 100644 index 000000000000..280b512ea348 --- /dev/null +++ b/src/test/java/com/thealgorithms/sorts/InsertionSortTest.java @@ -0,0 +1,114 @@ +package com.thealgorithms.sorts; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.function.Function; + +import static org.junit.jupiter.api.Assertions.*; + +class InsertionSortTest { + private InsertionSort insertionSort; + + @BeforeEach + void setUp() { + insertionSort = new InsertionSort(); + } + + @Test + void insertionSortSortEmptyArrayShouldPass() { + testEmptyArray(insertionSort::sort); + testEmptyArray(insertionSort::sentinelSort); + } + + private void testEmptyArray(Function sortAlgorithm) { + Integer[] array = {}; + Integer[] sorted = sortAlgorithm.apply(array); + Integer[] expected = {}; + assertArrayEquals(expected, sorted); + assertTrue(SortUtils.isSorted(sorted)); + } + + @Test + void insertionSortClassicalSortSingleValueArrayShouldPass() { + testSingleValue(insertionSort::sort); + testSingleValue(insertionSort::sentinelSort); + } + + private void testSingleValue(Function sortAlgorithm) { + Integer[] array = {7}; + Integer[] sorted = sortAlgorithm.apply(array); + Integer[] expected = {7}; + assertArrayEquals(expected, sorted); + assertTrue(SortUtils.isSorted(sorted)); + } + + @Test + void insertionSortClassicalWithIntegerArrayShouldPass() { + testIntegerArray(insertionSort::sort); + testIntegerArray(insertionSort::sentinelSort); + } + + private void testIntegerArray(Function sortAlgorithm) { + Integer[] array = {49, 4, 36, 9, 144, 1}; + Integer[] sorted = sortAlgorithm.apply(array); + Integer[] expected = {1, 4, 9, 36, 49, 144}; + assertArrayEquals(expected, sorted); + assertTrue(SortUtils.isSorted(sorted)); + } + + @Test + void insertionSortClassicalForArrayWithNegativeValuesShouldPass() { + testWithNegativeValues(insertionSort::sort); + testWithNegativeValues(insertionSort::sentinelSort); + } + + private void testWithNegativeValues(Function sortAlgorithm) { + Integer[] array = {49, -36, -144, -49, 1, 9}; + Integer[] sorted = sortAlgorithm.apply(array); + Integer[] expected = {-144, -49, -36, 1, 9, 49}; + assertArrayEquals(expected, sorted); + assertTrue(SortUtils.isSorted(sorted)); + } + + @Test + void insertionSortClassicalForArrayWithDuplicateValuesShouldPass() { + testWithDuplicates(insertionSort::sort); + testWithDuplicates(insertionSort::sentinelSort); + } + + private void testWithDuplicates(Function sortAlgorithm) { + Integer[] array = {36, 1, 49, 1, 4, 9}; + Integer[] sorted = sortAlgorithm.apply(array); + Integer[] expected = {1, 1, 4, 9, 36, 49}; + assertArrayEquals(expected, sorted); + assertTrue(SortUtils.isSorted(sorted)); + } + + @Test + void insertionSortClassicalWithStringArrayShouldPass() { + testWithStringArray(insertionSort::sort); + testWithStringArray(insertionSort::sentinelSort); + } + + private void testWithStringArray(Function sortAlgorithm) { + String[] array = {"c", "a", "e", "b", "d"}; + String[] sorted = sortAlgorithm.apply(array); + String[] expected = {"a", "b", "c", "d", "e"}; + assertArrayEquals(expected, sorted); + assertTrue(SortUtils.isSorted(sorted)); + } + + @Test + void insertionSortClassicalWithRandomArrayPass() { + testWithRandomArray(insertionSort::sort); + testWithRandomArray(insertionSort::sentinelSort); + } + + private void testWithRandomArray(Function sortAlgorithm) { + int randomSize = (int) (SortUtilsRandomGenerator.generateDouble() * 10_000); + Double[] array = SortUtilsRandomGenerator.generateArray(randomSize); + Double[] sorted = sortAlgorithm.apply(array); + assertTrue(SortUtils.isSorted(sorted)); + } +} diff --git a/src/test/java/com/thealgorithms/sorts/SortUtilsRandomGeneratorTest.java b/src/test/java/com/thealgorithms/sorts/SortUtilsRandomGeneratorTest.java new file mode 100644 index 000000000000..e694a2e41394 --- /dev/null +++ b/src/test/java/com/thealgorithms/sorts/SortUtilsRandomGeneratorTest.java @@ -0,0 +1,31 @@ +package com.thealgorithms.sorts; + +import org.junit.jupiter.api.RepeatedTest; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class SortUtilsRandomGeneratorTest { + + @RepeatedTest(1000) + void generateArray() { + int size = 1_000; + Double[] doubles = SortUtilsRandomGenerator.generateArray(size); + assertThat(doubles).hasSize(size); + assertThat(doubles).doesNotContainNull(); + } + + @Test + void generateArrayEmpty() { + int size = 0; + Double[] doubles = SortUtilsRandomGenerator.generateArray(size); + assertThat(doubles).hasSize(size); + } + + @RepeatedTest(1000) + void generateDouble() { + Double randomDouble = SortUtilsRandomGenerator.generateDouble(); + assertThat(randomDouble).isBetween(0.0, 1.0); + assertThat(randomDouble).isNotEqualTo(1.0); + } +} \ No newline at end of file diff --git a/src/test/java/com/thealgorithms/sorts/SortUtilsTest.java b/src/test/java/com/thealgorithms/sorts/SortUtilsTest.java new file mode 100644 index 000000000000..8a1de72165ac --- /dev/null +++ b/src/test/java/com/thealgorithms/sorts/SortUtilsTest.java @@ -0,0 +1,44 @@ +package com.thealgorithms.sorts; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class SortUtilsTest { + + @Test + void isSortedEmptyArray() { + Double[] emptyArray = {}; + assertTrue(SortUtils.isSorted(emptyArray)); + } + + @Test + void isSortedWithSingleElement() { + Double[] singleElementArray = {1.0}; + assertTrue(SortUtils.isSorted(singleElementArray)); + } + + @Test + void isSortedTrue() { + Integer[] array = {1, 1, 2, 3, 5, 8, 11}; + assertTrue(SortUtils.isSorted(array)); + + Integer[] identicalArray = {1, 1, 1, 1, 1}; + assertTrue(SortUtils.isSorted(identicalArray)); + + Double[] doubles = {-15.123, -15.111, 0.0, 0.12, 0.15}; + assertTrue(SortUtils.isSorted(doubles)); + } + + @Test + void isSortedFalse() { + Double[] array = {1.0, 3.0, -0.15}; + assertFalse(SortUtils.isSorted(array)); + + Integer[] array2 = {14, 15, 16, 1}; + assertFalse(SortUtils.isSorted(array2)); + + Integer[] array3 = {5, 4, 3, 2, 1}; + assertFalse(SortUtils.isSorted(array3)); + } +} \ No newline at end of file From 28aa2886ee4b9b1b44712be3449e320c83b1f5ff Mon Sep 17 00:00:00 2001 From: narek_karapetian Date: Fri, 21 Oct 2022 22:44:29 +0400 Subject: [PATCH 3/6] little fix --- .../com/thealgorithms/sorts/SortUtilsRandomGeneratorTest.java | 2 +- src/test/java/com/thealgorithms/sorts/SortUtilsTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/com/thealgorithms/sorts/SortUtilsRandomGeneratorTest.java b/src/test/java/com/thealgorithms/sorts/SortUtilsRandomGeneratorTest.java index e694a2e41394..30fd22c2da81 100644 --- a/src/test/java/com/thealgorithms/sorts/SortUtilsRandomGeneratorTest.java +++ b/src/test/java/com/thealgorithms/sorts/SortUtilsRandomGeneratorTest.java @@ -28,4 +28,4 @@ void generateDouble() { assertThat(randomDouble).isBetween(0.0, 1.0); assertThat(randomDouble).isNotEqualTo(1.0); } -} \ No newline at end of file +} diff --git a/src/test/java/com/thealgorithms/sorts/SortUtilsTest.java b/src/test/java/com/thealgorithms/sorts/SortUtilsTest.java index 8a1de72165ac..bb558727906b 100644 --- a/src/test/java/com/thealgorithms/sorts/SortUtilsTest.java +++ b/src/test/java/com/thealgorithms/sorts/SortUtilsTest.java @@ -41,4 +41,4 @@ void isSortedFalse() { Integer[] array3 = {5, 4, 3, 2, 1}; assertFalse(SortUtils.isSorted(array3)); } -} \ No newline at end of file +} From 05e802f0c0cb504f096c54f927beab42689a0096 Mon Sep 17 00:00:00 2001 From: narek_karapetian Date: Fri, 25 Nov 2022 20:37:11 +0400 Subject: [PATCH 4/6] simplify heap sort --- .../com/thealgorithms/sorts/HeapSort.java | 141 ++++-------------- .../com/thealgorithms/sorts/HeapSortTest.java | 94 +++++++++--- 2 files changed, 110 insertions(+), 125 deletions(-) diff --git a/src/main/java/com/thealgorithms/sorts/HeapSort.java b/src/main/java/com/thealgorithms/sorts/HeapSort.java index 1a96eaa39e99..0946f160760f 100644 --- a/src/main/java/com/thealgorithms/sorts/HeapSort.java +++ b/src/main/java/com/thealgorithms/sorts/HeapSort.java @@ -1,126 +1,51 @@ 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); + int n = unsorted.length; + heapify(unsorted, n); + while (n > 1) { + swap(unsorted, 1, n--); + siftDown(unsorted, 1, n); + } + return unsorted; } - @Override - public > List sort(List unsorted) { - int size = unsorted.size(); - - @SuppressWarnings("unchecked") - Heap heap = new Heap<>( - unsorted.toArray((T[]) new Comparable[unsorted.size()]) - ); + private static > void heapify(T[] unsorted, int n) { + for (int k = n / 2; k >= 1; k--) + siftDown(unsorted, k, n); + } - 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); + 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; } + } - return sorted; + 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; } - /** - * 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 > 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 7b34c59f6b2b..71a5ec3a7e66 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 }; + private HeapSort heapSort; + + @BeforeEach + void setUp() { + heapSort = new HeapSort(); + } + + @Test + void shouldAcceptWhenEmptyArrayIsPassed() { + Integer[] array = new Integer[]{}; + Integer[] expected = new Integer[]{}; + + Integer[] sorted = heapSort.sort(array); + + 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); - Integer[] expected = { 1, 4, 9, 36, 49, 144 }; + assertArrayEquals(expected, sorted); } - - @Test - void testHeapSortCase2() { - Integer[] array = { }; + + @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); - Integer[] expected = { }; + assertArrayEquals(expected, sorted); } - - @Test - void testHeapSortCase3 () { - Integer[] array = { -3, 5, 3, 4, 3, 7, 40, -20, 30, 0 }; + + @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); - Integer[] expected = { -20, -3, 0, 3, 3, 4, 5, 7, 30, 40 }; + 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)); + } + } From d669c4a4f4c97599fbaa0cbe5d15542555bae1cf Mon Sep 17 00:00:00 2001 From: Debasish Biswas Date: Fri, 25 Nov 2022 22:31:22 +0530 Subject: [PATCH 5/6] Update src/main/java/com/thealgorithms/sorts/HeapSort.java --- src/main/java/com/thealgorithms/sorts/HeapSort.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/thealgorithms/sorts/HeapSort.java b/src/main/java/com/thealgorithms/sorts/HeapSort.java index 0946f160760f..48185751addd 100644 --- a/src/main/java/com/thealgorithms/sorts/HeapSort.java +++ b/src/main/java/com/thealgorithms/sorts/HeapSort.java @@ -25,8 +25,9 @@ public > T[] sort(T[] unsorted) { } private static > void heapify(T[] unsorted, int n) { - for (int k = n / 2; k >= 1; k--) + for (int k = n / 2; k >= 1; k--) { siftDown(unsorted, k, n); + } } private static > void siftDown(T[] unsorted, int k, int n) { From 84e4e5f49a7a5f07ecccee30f75b9f185824c941 Mon Sep 17 00:00:00 2001 From: Debasish Biswas Date: Fri, 25 Nov 2022 22:31:28 +0530 Subject: [PATCH 6/6] Update src/main/java/com/thealgorithms/sorts/HeapSort.java --- src/main/java/com/thealgorithms/sorts/HeapSort.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/thealgorithms/sorts/HeapSort.java b/src/main/java/com/thealgorithms/sorts/HeapSort.java index 48185751addd..eec705ba476a 100644 --- a/src/main/java/com/thealgorithms/sorts/HeapSort.java +++ b/src/main/java/com/thealgorithms/sorts/HeapSort.java @@ -33,8 +33,12 @@ private static > void heapify(T[] unsorted, int 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; + if (j < n && less(unsorted, j, j + 1)) { + j++; + } + if (!less(unsorted, k, j)) { + break; + } swap(unsorted, k, j); k = j; }