Skip to content

Commit

Permalink
binary heap
Browse files Browse the repository at this point in the history
  • Loading branch information
giovannymassuia committed Jun 21, 2024
1 parent 3130433 commit d83794c
Show file tree
Hide file tree
Showing 3 changed files with 309 additions and 0 deletions.
119 changes: 119 additions & 0 deletions go-dsa/heap/binary_heap.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package heap

// BinaryHeap
// Arr[(i-1)/2] Returns the parent node
// Arr[(2*i)+1] Returns the left child node
// Arr[(2*i)+2] Returns the right child node
type BinaryHeap struct {
data []int
capacity int
size int
heapType HeapType
}

func NewBinaryHeap(capacity int, heapType HeapType) *BinaryHeap {
return &BinaryHeap{
data: make([]int, capacity),
capacity: capacity,
size: 0,
heapType: heapType,
}
}

func (h *BinaryHeap) GetData() []int {
return h.data[:h.size]
}

func (h *BinaryHeap) Peek() int {
if h.size == 0 {
return -1
}
return h.data[0]
}

func (h *BinaryHeap) Pop() int {
if h.size == 0 {
return -1
}

// swap the first value with the last value
swap(h.data, 0, h.size-1)
h.size--

// fix the heap property if it is violated
h.heapify(0)

return h.data[h.size]
}

func (h *BinaryHeap) heapify(index int) {
left := getLeftChildIndex(index)
right := getRightChildIndex(index)
smallest := index

if left < h.size && h.compare(h.data[left], h.data[smallest]) {
smallest = left
}

if right < h.size && h.compare(h.data[right], h.data[smallest]) {
smallest = right
}

if smallest != index {
swap(h.data, index, smallest)
h.heapify(smallest)
}
}

func (h *BinaryHeap) Insert(value int) bool {
if h.size == h.capacity {
return false
}

// first, insert the new element at the end
h.data[h.size] = value
h.size++

// fix the min heap property if it is violated
i := h.size - 1
for i != 0 && !h.compare(h.data[getParentIndex(i)], h.data[i]) {
swap(h.data, i, getParentIndex(i))
i = getParentIndex(i)
}

return true
}

func (h *BinaryHeap) Delete(value int) {
for i := 0; i < h.size; i++ {
if h.data[i] == value {
swap(h.data, i, h.size-1)
h.size--
h.heapify(i)
break
}
}
}

func (h *BinaryHeap) compare(a, b int) bool {
if h.heapType == MinHeap {
return a < b
}
return a > b
}

func swap(arr []int, i, j int) {
arr[i], arr[j] = arr[j], arr[i]
}

func getParentIndex(index int) int {
return (index - 1) / 2
}

func getLeftChildIndex(index int) int {
return (2 * index) + 1
}

func getRightChildIndex(index int) int {
return (2 * index) + 2
}
167 changes: 167 additions & 0 deletions go-dsa/heap/binary_heap_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
package heap

import (
"testing"
)

func TestMaxBinaryHeap(t *testing.T) {
heap := NewBinaryHeap(10, MaxHeap)

heap.Insert(10)
heap.Insert(20)
heap.Insert(15)
heap.Insert(40)
heap.Insert(50)
heap.Insert(100)
heap.Insert(25)
heap.Insert(45)

// Array: [100, 45, 50, 40, 20, 15, 25, 10]
/* Tree representation
100
/ \
45 50
/ \ / \
40 20 15 25
*/

tests := []struct {
name string
expectResult int
expectArray []int
}{
{"Peek", 100, []int{100, 45, 50, 40, 20, 15, 25, 10}},
/* Tree after peek
100
/ \
45 50
/ \ / \
40 20 15 25
*/
{"Pop", 100, []int{50, 45, 25, 40, 20, 15, 10}},
/* Tree after pop
50
/ \
45 25
/ \ / \
40 20 15 10
*/
{"Pop", 50, []int{45, 40, 25, 10, 20, 15}},
/* Tree after pop
45
/ \
40 25
/ \ / \
10 20 15
*/
{"Delete", 45, []int{45, 20, 25, 10, 15}},
/* Tree after delete
45
/ \
20 25
/ \ /
10 15
*/
{"Peek", 45, []int{45, 20, 25, 10, 15}},
/* Tree after peek
45
/ \
20 25
/ \ /
10 15
*/
{"Pop", 45, []int{25, 20, 15, 10}},
/* Tree after pop
25
/ \
20 15
/
10
*/
{"Pop", 25, []int{20, 10, 15}},
/* Tree after pop
20
/ \
10 15
*/
}

// should run tests in order they are defined
runTests(t, tests, heap, 40)
}

func TestMinBinaryHeap(t *testing.T) {
heap := NewBinaryHeap(10, MinHeap)

heap.Insert(10)
heap.Insert(20)
heap.Insert(15)
heap.Insert(40)
heap.Insert(50)
heap.Insert(100)
heap.Insert(25)
heap.Insert(45)

// Array: [10, 20, 15, 40, 50, 100, 25, 45]
/* Tree representation
10
/ \
20 15
/ \ / \
40 50 100 25
*/

tests := []struct {
name string
expectResult int
expectArray []int
}{
{"Peek", 10, []int{10, 20, 15, 40, 50, 100, 25, 45}},
{"Pop", 10, []int{15, 20, 25, 40, 50, 100, 45}},
{"Pop", 15, []int{20, 40, 25, 45, 50, 100}},
{"Delete", 25, []int{25, 40, 100, 45, 50}},
{"Peek", 25, []int{25, 40, 100, 45, 50}},
{"Pop", 25, []int{40, 45, 100, 50}},
{"Pop", 40, []int{45, 50, 100}},
}

// should run tests in order they are defined
runTests(t, tests, heap, 20)
}

func runTests(t *testing.T, tests []struct {
name string
expectResult int
expectArray []int
}, heap *BinaryHeap, deleteValue int) {
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
var result int
if test.name == "Peek" {
result = heap.Peek()
} else if test.name == "Pop" {
result = heap.Pop()
} else if test.name == "Delete" {
heap.Delete(deleteValue)
result = heap.Peek()
}

if result != test.expectResult {
t.Errorf("Expected %d but got %d", test.expectResult, result)
}
if !compareArray(heap.GetData(), test.expectArray) {
t.Errorf("Expected %v but got %v", test.expectArray, heap.GetData())
}
})
}
}

func compareArray(actual, expected []int) bool {
for i := 0; i < len(expected); i++ {
if actual[i] != expected[i] {
return false
}
}

return true
}
23 changes: 23 additions & 0 deletions go-dsa/heap/heap.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package heap

// insert -> O(logN)
// extract / pop -> O(1) - max for max heap, min for min heap
// delete -> O(logN)
// heapify -> O(log N)

type Heap interface {
Insert(int)
Pop() int
Peek() int
Delete(int)
}

// heap type enum
type HeapType int

const (
MinHeap HeapType = iota
MaxHeap
)


0 comments on commit d83794c

Please sign in to comment.