diff --git a/data-structures/src/main/java/com/baeldung/minmaxheap/MinMaxHeap.java b/data-structures/src/main/java/com/baeldung/minmaxheap/MinMaxHeap.java new file mode 100644 index 0000000000..124183afe6 --- /dev/null +++ b/data-structures/src/main/java/com/baeldung/minmaxheap/MinMaxHeap.java @@ -0,0 +1,344 @@ +package com.baeldung.minmaxheap; + +import java.util.*; + +/** + * Created by arash on 15.06.21. + */ + +public class MinMaxHeap> { + private List array; + private int capacity; + private int indicator; + + MinMaxHeap(int capacity) { + array = new ArrayList<>(); + this.capacity = capacity; + indicator = 1; + } + + MinMaxHeap(List array) { + this.array = array; + this.capacity = array.size(); + this.indicator = array.size() + 1; + } + + public List getMinMaxHeap() { + return array; + } + + public List create() { + for (int i = Math.floorDiv(array.size(), 2); i >= 1; i--) { + pushDown(array, i); + } + return array; + } + + private void pushDown(List array, int i) { + if (isEvenLevel(i)) { + pushDownMin(array, i); + } else { + pushDownMax(array, i); + } + } + + private void pushDownMin(List h, int i) { + while (getLeftChildIndex(i) < indicator) { + int indexOfSmallest = getIndexOfSmallestChildOrGrandChild(h, i); + if (h.get(indexOfSmallest - 1).compareTo(h.get(i - 1)) < 0) { + if (getParentIndex(getParentIndex(indexOfSmallest)) == i) { + if (h.get(indexOfSmallest - 1).compareTo(h.get(i - 1)) < 0) { + swap(indexOfSmallest - 1, i - 1, h); + if (h.get(indexOfSmallest - 1).compareTo(h.get(getParentIndex(indexOfSmallest) - 1)) > 0) { + swap(indexOfSmallest - 1, getParentIndex(indexOfSmallest) - 1, h); + } + } + } else if (h.get(indexOfSmallest - 1).compareTo(h.get(i - 1)) < 0) { + swap(indexOfSmallest - 1, i - 1, h); + } + } else { + break; + } + i = indexOfSmallest; + } + } + + private void pushDownMax(List h, int i) { + while (getLeftChildIndex(i) < indicator) { + int indexOfGreatest = getIndexOfGreatestChildOrGrandChild(h, i); + if (h.get(indexOfGreatest - 1).compareTo(h.get(i - 1)) > 0) { + if (getParentIndex(getParentIndex(indexOfGreatest)) == i) { + if (h.get(indexOfGreatest - 1).compareTo(h.get(i - 1)) > 0) { + swap(indexOfGreatest - 1, i - 1, h); + if (h.get(indexOfGreatest - 1).compareTo(h.get(getParentIndex(indexOfGreatest) - 1)) < 0) { + swap(indexOfGreatest - 1, getParentIndex(indexOfGreatest) - 1, h); + } + } + } else if (h.get(indexOfGreatest - 1).compareTo(h.get(i - 1)) > 0) { + swap(indexOfGreatest - 1, i - 1, h); + } + } else { + break; + } + i = indexOfGreatest; + } + } + + private void swap(int i, int j, List h) { //switch data at x with data at y + T temp = h.get(i); + h.set(i, h.get(j)); + h.set(j, temp); + } + + private int getLeftChildIndex(int i) { + return 2 * i; + } + + private int getRightChildIndex(int i) { + return ((2 * i) + 1); + } + + private int getParentIndex(int i) { + return i / 2; + } + + private int getGrandparentIndex(int i) { + return i / 4; + } + + private boolean isEvenLevel(int i) { + return logBase2(i) % 2 == 0; + } + + private int logBase2(int num) { + return (int) (Math.log(num) / Math.log(2)); + } + + private int getMinChildIndex(int i) { + return array.get(getLeftChildIndex(i)).compareTo(array.get(getRightChildIndex(i))) < 0 ? getLeftChildIndex(i) : getRightChildIndex(i); + } + + private int getMaxChildIndex(int i) { + return array.get(getLeftChildIndex(i)).compareTo(array.get(getRightChildIndex(i))) > 0 ? getLeftChildIndex(i) : getRightChildIndex(i); + } + + private int getIndexOfSmallestChildOrGrandChild(List h, int i) { + int minIndex = getLeftChildIndex(i); + T minValue = h.get(minIndex - 1); + + if (getRightChildIndex(i) < indicator) { + if (h.get(getRightChildIndex(i) - 1).compareTo(minValue) < 0) { + minValue = h.get(getRightChildIndex(i)); + minIndex = getRightChildIndex(i); + } + } else { + return minIndex; + } + + if (getLeftChildIndex(getLeftChildIndex(i)) < indicator) { + if (h.get(getLeftChildIndex(getLeftChildIndex(i)) - 1).compareTo(minValue) < 0) { + minValue = h.get(getLeftChildIndex(getLeftChildIndex(i)) - 1); + minIndex = getLeftChildIndex(getLeftChildIndex(i)); + } + } else { + return minIndex; //if no leftmost grandchild + } + + if (getRightChildIndex(getLeftChildIndex(i)) < indicator) { + if (h.get(getRightChildIndex(getLeftChildIndex(i)) - 1).compareTo(minValue) < 0) { + minValue = h.get(getRightChildIndex(getLeftChildIndex(i)) - 1); + minIndex = getRightChildIndex(getLeftChildIndex(i)); + } + } else { + return minIndex; //if no left-right grandchild + } + + if (getLeftChildIndex(getRightChildIndex(i)) < indicator) { + if (h.get(getLeftChildIndex(getRightChildIndex(i)) - 1).compareTo(minValue) < 0) { + minValue = h.get(getLeftChildIndex(getRightChildIndex(i)) - 1); + minIndex = getLeftChildIndex(getRightChildIndex(i)); + } + } else { + return minIndex; //if no right-left grandchild + } + + if (getRightChildIndex(getRightChildIndex(i)) < indicator) { + if (h.get(getRightChildIndex(getRightChildIndex(i)) - 1).compareTo(minValue) < 0) { + minValue = h.get(getRightChildIndex(getRightChildIndex(i)) - 1); + minIndex = getRightChildIndex(getRightChildIndex(i)); + } + } else { + return minIndex; + } + + return minIndex; + } + + private int getIndexOfGreatestChildOrGrandChild(List h, int i) { + int maxIndex = getLeftChildIndex(i); //we know left child exists + T maxValue = h.get(maxIndex - 1); + + if (getRightChildIndex(i) < indicator) { + if (h.get(getRightChildIndex(i) - 1).compareTo(maxValue) > 0) { + maxValue = h.get(getRightChildIndex(i) - 1); + maxIndex = getRightChildIndex(i); + } + } else { + return maxIndex; + } + + if (getLeftChildIndex(getLeftChildIndex(i)) < indicator) { + if (h.get(getLeftChildIndex(getLeftChildIndex(i)) - 1).compareTo(maxValue) > 0) { + maxValue = h.get(getLeftChildIndex(getLeftChildIndex(i)) - 1); + maxIndex = getLeftChildIndex(getLeftChildIndex(i)); + } + } else { + return maxIndex; //if no leftmost grandchild + } + + if (getRightChildIndex(getLeftChildIndex(i)) < indicator) { + if (h.get(getRightChildIndex(getLeftChildIndex(i)) - 1).compareTo(maxValue) > 0) { + maxValue = h.get(getRightChildIndex(getLeftChildIndex(i)) - 1); + maxIndex = getRightChildIndex(getLeftChildIndex(i)); + } + } else { + return maxIndex; //if no left-right grandchild + } + + if (getLeftChildIndex(getRightChildIndex(i)) < indicator) { + if (h.get(getLeftChildIndex(getRightChildIndex(i)) - 1).compareTo(maxValue) > 0) { + maxValue = h.get(getLeftChildIndex(getRightChildIndex(i)) - 1); + maxIndex = getLeftChildIndex(getRightChildIndex(i)); + } + } else { + return maxIndex; + } + + if (getRightChildIndex(getRightChildIndex(i)) < indicator) { + if (h.get(getRightChildIndex(getRightChildIndex(i)) - 1).compareTo(maxValue) > 0) { + maxValue = h.get(getRightChildIndex(getRightChildIndex(i)) - 1); + maxIndex = getRightChildIndex(getRightChildIndex(i)); + } + } else { + return maxIndex; + } + + return maxIndex; + } + + + public boolean isFull() { + return indicator == this.capacity + 1; + } + + public boolean isEmpty() { + return indicator == 1; + } + + public void insert(T item) { + if (isEmpty()) { + array.add(item); + indicator++; + } else if (!isFull()) { + array.add(item); + pushUp(array, indicator); + indicator++; + } else { + throw new RuntimeException("invalid operation !!!"); + } + } + + private void pushUpMin(List h, int i) { + while (hasGrandparent(i) && h.get(i - 1).compareTo(h.get(getGrandparentIndex(i) - 1)) < 0) { + swap(i - 1, getGrandparentIndex(i) - 1, h); + i = getGrandparentIndex(i); + } + } + + private void pushUpMax(List h, int i) { + while (hasGrandparent(i) && h.get(i - 1).compareTo(h.get(getGrandparentIndex(i) - 1)) > 0) { + swap(i - 1, getGrandparentIndex(i) - 1, h); + i = getGrandparentIndex(i); + } + } + + private boolean hasGrandparent(int i) { + return getParentIndex(i) > 1; + } + + private void pushUp(List h, int i) { + if (i != 1) { + if (isEvenLevel(i)) { + if (h.get(i - 1).compareTo(h.get(getParentIndex(i) - 1)) < 0) { + pushUpMin(h, i); + } else { + swap(i - 1, getParentIndex(i) - 1, h); + i = getParentIndex(i); + pushUpMax(h, i); + } + } else if (h.get(i - 1).compareTo(h.get(getParentIndex(i) - 1)) > 0) { + pushUpMax(h, i); + } else { + swap(i - 1, getParentIndex(i) - 1, h); + i = getParentIndex(i); + pushUpMin(h, i); + } + } + } + + public T min() { + if (!isEmpty()) { + return array.get(0); + } + return null; + } + + public T max() { + if (!isEmpty()) { + if (indicator == 2) { + return array.get(0); + } + if (indicator == 3) { + return array.get(1); + } + return array.get(1).compareTo(array.get(2)) < 0 ? array.get(2) : array.get(1); + } + return null; + } + + public T removeMin() { + T min = min(); + if (min != null) { + if (indicator == 2) { + array.remove(indicator--); + return min; + } + array.set(0, array.get(--indicator - 1)); + array.remove(indicator - 1); + pushDown(array, 1); + } + return min; + } + + public T removeMax() { + T max = max(); + if (max != null) { + int maxIndex; + if (indicator == 2) { + maxIndex = 0; + array.remove(--indicator - 1); + return max; + } else if (indicator == 3) { + maxIndex = 1; + array.remove(--indicator - 1); + return max; + } else { + maxIndex = array.get(1).compareTo(array.get(2)) < 0 ? 2 : 1; + } + array.set(maxIndex, array.get(--indicator - 1)); + array.remove(indicator - 1); + pushDown(array, maxIndex + 1); + } + return max; + } +} diff --git a/data-structures/src/test/java/com/baeldung/minmaxheap/MinMaxHeapUnitTest.java b/data-structures/src/test/java/com/baeldung/minmaxheap/MinMaxHeapUnitTest.java new file mode 100644 index 0000000000..1682db63be --- /dev/null +++ b/data-structures/src/test/java/com/baeldung/minmaxheap/MinMaxHeapUnitTest.java @@ -0,0 +1,32 @@ +package com.baeldung.minmaxheap; + +import org.junit.Assert; +import org.junit.Test; +import java.util.Arrays; +import java.util.List; + + +public class MinMaxHeapUnitTest { + + @Test + public void givenUnOrderedArray_WhenCreateMinMaxHeap_ThenIsEqualWithMinMaxHeapOrdered() { + List list = Arrays.asList(34, 12, 28, 9, 30, 19, 1, 40); + MinMaxHeap minMaxHeap = new MinMaxHeap<>(list); + minMaxHeap.create(); + Assert.assertEquals(Arrays.asList(1, 40, 34, 9, 30, 19, 28, 12), list); + } + + @Test + public void givenNewElement_WhenInserted_ThenIsEqualWithMinMaxHeapOrdered() { + MinMaxHeap minMaxHeap = new MinMaxHeap(8); + minMaxHeap.insert(34); + minMaxHeap.insert(12); + minMaxHeap.insert(28); + minMaxHeap.insert(9); + minMaxHeap.insert(30); + minMaxHeap.insert(19); + minMaxHeap.insert(1); + minMaxHeap.insert(40); + Assert.assertEquals(Arrays.asList(1, 40, 28, 12, 30, 19, 9, 34), minMaxHeap.getMinMaxHeap()); + } +}