Skip to main content

Heap Sort

package algorithmdesignmanualbook.sorting

import utils.assertArraysSame
import java.lang.Integer.max
import java.lang.Integer.min
import java.util.*
import kotlin.test.assertTrue

class HeapSort {
private var array = emptyArray<Int>()
private var nextIndex = 0

fun peekArray() = array.copyOf(array.size)

fun add(element: Int) {
if (array.isEmpty() || nextIndex > array.lastIndex) {
growArray()
}
array[nextIndex] = element
bubbleUp(nextIndex)
nextIndex++
}

private fun growArray() {
// Size is always index + 1
val temp = Array(nextIndex + 1) { 0 }
System.arraycopy(array, 0, temp, 0, array.size)
array = temp
}

private fun shrinkArray() {
val temp = Array(array.size - 1) { 0 }
System.arraycopy(array, 0, temp, 0, array.size - 1)
array = temp
nextIndex--
}

private fun bubbleUp(index: Int) {
var currentIndex = index
var parentIndex = parentIndexOfIndex(currentIndex)
while (currentIndex != parentIndex && array[parentIndex] > array[currentIndex]) {
swap(parentIndex, currentIndex)
currentIndex = parentIndex
parentIndex = parentIndexOfIndex(currentIndex)
}
}

private fun swap(from: Int, to: Int) {
println("Swapping index:$from-->$to; value:${array[from]}-->${array[to]}")
val temp = array[from]
array[from] = array[to]
array[to] = temp
}

private fun parentIndexOfIndex(index: Int): Int = if (index % 2 == 0) max(0, index / 2 - 1) else index / 2

private fun indexOfLeftChildOfIndex(index: Int): Int = index * 2 + 1

private fun indexOfRightChildOfIndex(index: Int): Int = index * 2 + 2

fun print() = println(Arrays.toString(array))

fun peekMinValue() = array[0]

fun deleteMin() {
// Bring right most element to the root
swap(0, array.lastIndex)
// Delete the last element
shrinkArray()
// then bubble root element down
bubbleDown()
}

private fun bubbleDown() {
var currentIndex = 0
while (currentIndex < array.size) {
val leftIndex = indexOfLeftChildOfIndex(currentIndex)
val rightIndex = indexOfRightChildOfIndex(currentIndex)
// Left index is filled first so it must be less than last index
if (leftIndex > array.lastIndex) {
break
}
val leftValue = array[leftIndex]
val rightValue = array.getOrNull(rightIndex) ?: Integer.MAX_VALUE

// WARNING: MUST compare with both left and right node and swap with the minimum
val minValue = min(leftValue, rightValue)

// Satisfactory position
if (minValue >= array[currentIndex]) {
break
}

if (minValue == leftValue) {
// swap with left
swap(leftIndex, currentIndex)
currentIndex = leftIndex
} else if (minValue == rightValue) {
// swap with right
swap(rightIndex, currentIndex)
currentIndex = rightIndex
} else {
break
}
}
}
}

fun main() {
val sort = HeapSort()
sort.add(20)
sort.add(30)
sort.add(40)
sort.add(50)
sort.add(60)
sort.print()
assertArraysSame(expected = arrayOf(20, 30, 40, 50, 60), actual = sort.peekArray())
sort.add(10)
sort.print()
assertArraysSame(expected = arrayOf(10, 30, 20, 50, 60, 40), actual = sort.peekArray())
assertTrue { sort.peekMinValue() == 10 }

sort.deleteMin()
assertArraysSame(expected = arrayOf(20, 30, 40, 50, 60), actual = sort.peekArray())

sort.deleteMin()
assertTrue { sort.peekMinValue() == 30 }
assertArraysSame(expected = arrayOf(30, 50, 40, 60), actual = sort.peekArray())
}

Updated on 2021-02-08