From 485b392cb31823e75e092d0b138b0ee0848f665a Mon Sep 17 00:00:00 2001 From: Miccah Date: Sat, 25 Dec 2021 04:03:50 -0600 Subject: [PATCH] adt: implement a binary MinHeap data structure (#12956) --- vlib/adt/README.md | 2 ++ vlib/adt/heap.v | 84 ++++++++++++++++++++++++++++++++++++++++++++ vlib/adt/heap_test.v | 68 +++++++++++++++++++++++++++++++++++ 3 files changed, 154 insertions(+) create mode 100644 vlib/adt/heap.v create mode 100644 vlib/adt/heap_test.v diff --git a/vlib/adt/README.md b/vlib/adt/README.md index 55293404b7..6aa01d81bf 100644 --- a/vlib/adt/README.md +++ b/vlib/adt/README.md @@ -22,6 +22,8 @@ println(stack) ## Currently Implemented Datatypes: - [x] Linked list +- [x] Doubly linked list - [x] Stack (LIFO) - [x] Queue (FIFO) +- [x] Min heap (priority queue) - [ ] ... diff --git a/vlib/adt/heap.v b/vlib/adt/heap.v new file mode 100644 index 0000000000..b5fd5a3658 --- /dev/null +++ b/vlib/adt/heap.v @@ -0,0 +1,84 @@ +module adt + +// MinHeap is a binary minimum heap data structure. +pub struct MinHeap { +mut: + data []T +} + +// insert adds an element to the heap. +pub fn (mut heap MinHeap) insert(item T) { + // push item to the end of the array + heap.data << item + // swap the new node with its parent until the heap is in order + mut child := heap.data.len - 1 + mut parent := heap.parent(child) + for heap.data[parent] > heap.data[child] { + heap.data[parent], heap.data[child] = heap.data[child], heap.data[parent] + child = parent + parent = heap.parent(child) + } +} + +// pop removes the top-most element from the heap. +pub fn (mut heap MinHeap) pop() ?T { + if heap.data.len == 0 { + return none + } else if heap.data.len == 1 { + return heap.data.pop() + } + item := heap.data[0] + // move last element to root + heap.data[0] = heap.data.pop() + // swap the new root with its minimum child until the heap is in order + mut parent := 0 + mut left := heap.left_child(parent) or { return item } + mut right := heap.right_child(parent) or { left } + for heap.data[parent] > heap.data[left] || heap.data[parent] > heap.data[right] { + // choose min for min heap + swap := if heap.data[left] <= heap.data[right] { left } else { right } + heap.data[parent], heap.data[swap] = heap.data[swap], heap.data[parent] + parent = swap + left = heap.left_child(parent) or { break } + right = heap.right_child(parent) or { left } + } + return item +} + +// peek gets the top-most element from the heap without removing it. +pub fn (heap MinHeap) peek() ?T { + if heap.data.len == 0 { + return none + } + return heap.data[0] +} + +// len returns the number of elements in the heap. +pub fn (heap MinHeap) len() int { + return heap.data.len +} + +// left_child is a helper function that returns the index of the left +// child given a parent idx, or none if there is no left child. +fn (heap MinHeap) left_child(idx int) ?int { + child := 2 * idx + 1 + if child >= heap.data.len { + return none + } + return child +} + +// right_child is a helper function that returns the index of the right +// child given a parent idx, or none if there is no right child. +fn (heap MinHeap) right_child(idx int) ?int { + child := 2 * idx + 2 + if child >= heap.data.len { + return none + } + return child +} + +// parent is a helper function that returns the parent index of the child. +fn (heap MinHeap) parent(idx int) int { + return (idx - 1) / 2 +} diff --git a/vlib/adt/heap_test.v b/vlib/adt/heap_test.v new file mode 100644 index 0000000000..51a535dc94 --- /dev/null +++ b/vlib/adt/heap_test.v @@ -0,0 +1,68 @@ +module adt + +fn test_min_heap() ? { + mut heap := MinHeap{} + heap.insert(2) + heap.insert(0) + heap.insert(8) + heap.insert(4) + heap.insert(1) + + assert heap.pop() ? == 0 + assert heap.pop() ? == 1 + assert heap.pop() ? == 2 + assert heap.pop() ? == 4 + assert heap.pop() ? == 8 + if _ := heap.pop() { + panic('expected none') + } +} + +struct Item { + data string + priority int +} + +fn (lhs Item) < (rhs Item) bool { + return rhs.priority < lhs.priority +} + +fn test_min_heap_custom() ? { + mut heap := MinHeap{} + heap.insert(Item{'buz', 10}) + heap.insert(Item{'qux', 0}) + heap.insert(Item{'baz', 50}) + heap.insert(Item{'foo', 100}) + heap.insert(Item{'bar', 80}) + + assert heap.pop() ?.data == 'foo' + assert heap.pop() ?.data == 'bar' + assert heap.pop() ?.data == 'baz' + assert heap.pop() ?.data == 'buz' + assert heap.pop() ?.data == 'qux' + if _ := heap.pop() { + panic('expected none') + } +} + +fn test_heap_len() ? { + mut heap := MinHeap{} + heap.insert(2) + assert heap.len() == 1 + heap.insert(0) + heap.insert(8) + heap.insert(4) + assert heap.len() == 4 + heap.insert(1) + + assert heap.len() == 5 + heap.pop() ? + heap.pop() ? + heap.pop() ? + assert heap.len() == 2 + heap.pop() ? + heap.pop() ? + assert heap.len() == 0 + heap.pop() or {} + assert heap.len() == 0 +}