Added graph search adlgorithms and Heap and PriorityQueue data structures
parent
726d2b923f
commit
e33b5b790f
@ -0,0 +1,103 @@
|
|||||||
|
package container
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.org/x/exp/constraints"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HeapItem[T any, V constraints.Integer] struct {
|
||||||
|
Value T
|
||||||
|
Priority V
|
||||||
|
}
|
||||||
|
|
||||||
|
// Heap represents a generic heap data structure.
|
||||||
|
type Heap[T any, V constraints.Integer] struct {
|
||||||
|
elements []HeapItem[T, V]
|
||||||
|
isMinHeap bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewHeap creates a new heap with the specified ordering.
|
||||||
|
// The isMinHeap parameter determines whether the heap is a min heap or a max heap.
|
||||||
|
// The heap is initially empty.
|
||||||
|
func NewHeap[T any, V constraints.Integer](isMinHeap bool) *Heap[T, V] {
|
||||||
|
return &Heap[T, V]{elements: []HeapItem[T, V]{}, isMinHeap: isMinHeap}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push adds an element to the heap.
|
||||||
|
func (h *Heap[T, V]) Push(element T, priority V) {
|
||||||
|
h.elements = append(h.elements, HeapItem[T, V]{element, priority})
|
||||||
|
h.heapifyUp()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pop removes and returns the root element from the heap.
|
||||||
|
func (h *Heap[T, V]) Pop() T {
|
||||||
|
if len(h.elements) == 0 {
|
||||||
|
var zero T // Return zero value of T
|
||||||
|
return zero
|
||||||
|
}
|
||||||
|
|
||||||
|
root := h.elements[0]
|
||||||
|
lastIndex := len(h.elements) - 1
|
||||||
|
h.elements[0] = h.elements[lastIndex]
|
||||||
|
h.elements = h.elements[:lastIndex]
|
||||||
|
|
||||||
|
h.heapifyDown()
|
||||||
|
return root.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
// Peek returns the root element without removing it.
|
||||||
|
func (h *Heap[T, V]) Peek() T {
|
||||||
|
if len(h.elements) == 0 {
|
||||||
|
var zero T // Return zero value of T
|
||||||
|
return zero
|
||||||
|
}
|
||||||
|
return h.elements[0].Value
|
||||||
|
}
|
||||||
|
|
||||||
|
// heapifyUp adjusts the heap after adding a new element.
|
||||||
|
func (h *Heap[T, V]) heapifyUp() {
|
||||||
|
index := len(h.elements) - 1
|
||||||
|
for index > 0 {
|
||||||
|
parentIndex := (index - 1) / 2
|
||||||
|
if h.compare(h.elements[index].Priority, h.elements[parentIndex].Priority) {
|
||||||
|
h.elements[index], h.elements[parentIndex] = h.elements[parentIndex], h.elements[index]
|
||||||
|
index = parentIndex
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// heapifyDown adjusts the heap after removing the root element.
|
||||||
|
func (h *Heap[T, V]) heapifyDown() {
|
||||||
|
index := 0
|
||||||
|
lastIndex := len(h.elements) - 1
|
||||||
|
for index < lastIndex {
|
||||||
|
leftChildIndex := 2*index + 1
|
||||||
|
rightChildIndex := 2*index + 2
|
||||||
|
|
||||||
|
var childIndex int
|
||||||
|
if leftChildIndex <= lastIndex {
|
||||||
|
childIndex = leftChildIndex
|
||||||
|
if rightChildIndex <= lastIndex && h.compare(h.elements[rightChildIndex].Priority, h.elements[leftChildIndex].Priority) {
|
||||||
|
childIndex = rightChildIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
if h.compare(h.elements[childIndex].Priority, h.elements[index].Priority) {
|
||||||
|
h.elements[index], h.elements[childIndex] = h.elements[childIndex], h.elements[index]
|
||||||
|
index = childIndex
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// compare compares two elements based on the heap type.
|
||||||
|
func (h *Heap[T, V]) compare(x, y V) bool {
|
||||||
|
if h.isMinHeap {
|
||||||
|
return x < y
|
||||||
|
}
|
||||||
|
return x > y
|
||||||
|
}
|
||||||
@ -0,0 +1,145 @@
|
|||||||
|
package container
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestPush tests the Push method of the Heap.
|
||||||
|
func TestPush(t *testing.T) {
|
||||||
|
heap := NewHeap[int, int](true) // Testing a min heap
|
||||||
|
heap.Push(3, 3)
|
||||||
|
heap.Push(1, 1)
|
||||||
|
heap.Push(2, 2)
|
||||||
|
|
||||||
|
expected := []int{1, 3, 2} // The expected min-heap state after pushing elements
|
||||||
|
for i, v := range heap.elements {
|
||||||
|
if v.Value != expected[i] {
|
||||||
|
t.Errorf("Push() test failed: expected %v at index %d, got %v", expected[i], i, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestPop tests the Pop method of the Heap.
|
||||||
|
func TestPop(t *testing.T) {
|
||||||
|
heap := NewHeap[int, int](true)
|
||||||
|
heap.Push(3, 3)
|
||||||
|
heap.Push(1, 1)
|
||||||
|
heap.Push(2, 2)
|
||||||
|
|
||||||
|
if val := heap.Pop(); val != 1 {
|
||||||
|
t.Errorf("Pop() test failed: expected 1, got %v", val)
|
||||||
|
}
|
||||||
|
|
||||||
|
if val := heap.Pop(); val != 2 {
|
||||||
|
t.Errorf("Pop() test failed: expected 2, got %v", val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestPeek tests the Peek method of the Heap.
|
||||||
|
func TestPeek(t *testing.T) {
|
||||||
|
heap := NewHeap[int, int](true)
|
||||||
|
heap.Push(3, 3)
|
||||||
|
heap.Push(1, 1)
|
||||||
|
heap.Push(2, 2)
|
||||||
|
|
||||||
|
if val := heap.Peek(); val != 1 {
|
||||||
|
t.Errorf("Peek() test failed: expected 1, got %v", val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if Peek() doesn't remove the element
|
||||||
|
if val := heap.Pop(); val != 1 {
|
||||||
|
t.Errorf("Peek() test failed: Peek() should not remove the element")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestHeapProperty tests if the heap maintains its property after operations.
|
||||||
|
func TestHeapProperty(t *testing.T) {
|
||||||
|
heap := NewHeap[int, int](true) // Testing a min heap
|
||||||
|
heap.Push(5, 5)
|
||||||
|
heap.Push(3, 3)
|
||||||
|
heap.Push(8, 8)
|
||||||
|
heap.Push(1, 1)
|
||||||
|
heap.Push(7, 7)
|
||||||
|
|
||||||
|
// After every Pop(), the next smallest element should come out
|
||||||
|
expectedOrder := []int{1, 3, 5, 7, 8}
|
||||||
|
for _, expected := range expectedOrder {
|
||||||
|
if val := heap.Pop(); val != expected {
|
||||||
|
t.Errorf("Heap property test failed: expected %v, got %v", expected, val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestNewHeap tests the NewHeap function.
|
||||||
|
func TestNewHeap(t *testing.T) {
|
||||||
|
// Test creating a min heap
|
||||||
|
minHeap := NewHeap[int, int](true)
|
||||||
|
if minHeap == nil {
|
||||||
|
t.Error("NewHeap() test failed: expected a non-nil heap")
|
||||||
|
}
|
||||||
|
if minHeap != nil && !minHeap.isMinHeap {
|
||||||
|
t.Error("NewHeap() test failed: expected a min heap")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test creating a max heap
|
||||||
|
maxHeap := NewHeap[int, int](false)
|
||||||
|
if maxHeap == nil {
|
||||||
|
t.Error("NewHeap() test failed: expected a non-nil heap")
|
||||||
|
}
|
||||||
|
if minHeap != nil && maxHeap.isMinHeap {
|
||||||
|
t.Error("NewHeap() test failed: expected a max heap")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestHeapifyUp tests the heapifyUp method of the Heap.
|
||||||
|
func TestHeapifyUp(t *testing.T) {
|
||||||
|
heap := NewHeap[int, int](true)
|
||||||
|
heap.elements = []HeapItem[int, int]{{2, 2}, {3, 3}, {1, 1}}
|
||||||
|
|
||||||
|
heap.heapifyUp()
|
||||||
|
|
||||||
|
expected := []int{1, 3, 2} // The expected min-heap state after heapifyUp
|
||||||
|
for i, v := range heap.elements {
|
||||||
|
if v.Value != expected[i] {
|
||||||
|
t.Errorf("heapifyUp() test failed: expected %v at index %d, got %v", expected[i], i, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestHeapifyUpEmptyHeap tests the heapifyUp method of an empty Heap.
|
||||||
|
func TestHeapifyUpEmptyHeap(t *testing.T) {
|
||||||
|
heap := NewHeap[int, int](true)
|
||||||
|
|
||||||
|
heap.heapifyUp()
|
||||||
|
|
||||||
|
if len(heap.elements) != 0 {
|
||||||
|
t.Errorf("heapifyUp() test failed: expected empty heap, got %v", heap.elements)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestHeapifyUpSingleElement tests the heapifyUp method of a Heap with a single element.
|
||||||
|
func TestHeapifyUpSingleElement(t *testing.T) {
|
||||||
|
heap := NewHeap[int, int](true)
|
||||||
|
heap.elements = []HeapItem[int, int]{{1, 1}}
|
||||||
|
|
||||||
|
heap.heapifyUp()
|
||||||
|
|
||||||
|
if len(heap.elements) != 1 || heap.elements[0].Value != 1 {
|
||||||
|
t.Errorf("heapifyUp() test failed: expected [1], got %v", heap.elements)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestHeapifyDown tests the heapifyDown method of the Heap.
|
||||||
|
func TestHeapifyDown(t *testing.T) {
|
||||||
|
heap := NewHeap[int, int](true)
|
||||||
|
heap.elements = []HeapItem[int, int]{{8, 8}, {1, 1}, {3, 3}, {5, 5}, {7, 7}}
|
||||||
|
|
||||||
|
heap.heapifyDown()
|
||||||
|
|
||||||
|
expected := []int{1, 5, 3, 8, 7} // The expected heap state after heapifyDown
|
||||||
|
for i, v := range heap.elements {
|
||||||
|
if v.Value != expected[i] {
|
||||||
|
t.Errorf("heapifyDown() test failed: expected %v at index %d, got %v", expected[i], i, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
package container
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.org/x/exp/constraints"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PriorityQueue represents a priority queue data structure.
|
||||||
|
type PriorityQueue[T any, V constraints.Integer] struct {
|
||||||
|
heap *Heap[T, V]
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPriorityQueue creates a new PriorityQueue instance.
|
||||||
|
// isMinQueue determines whether it is a min-priority queue (true) or a max-priority queue (false).
|
||||||
|
func NewPriorityQueue[T any, V constraints.Integer](isMinQueue bool) *PriorityQueue[T, V] {
|
||||||
|
return &PriorityQueue[T, V]{
|
||||||
|
heap: NewHeap[T, V](isMinQueue),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enqueue adds an element to the priority queue.
|
||||||
|
func (pq *PriorityQueue[T, V]) Enqueue(element T, priority V) {
|
||||||
|
pq.heap.Push(element, priority)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dequeue removes and returns the element with the highest priority from the queue.
|
||||||
|
func (pq *PriorityQueue[T, V]) Dequeue() T {
|
||||||
|
return pq.heap.Pop()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Peek returns the element with the highest priority without removing it from the queue.
|
||||||
|
func (pq *PriorityQueue[T, V]) Peek() T {
|
||||||
|
return pq.heap.Peek()
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEmpty returns true if the priority queue is empty.
|
||||||
|
func (pq *PriorityQueue[T, V]) IsEmpty() bool {
|
||||||
|
return len(pq.heap.elements) == 0
|
||||||
|
}
|
||||||
@ -0,0 +1,72 @@
|
|||||||
|
package container
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestEnqueue tests the Enqueue method of the PriorityQueue.
|
||||||
|
func TestEnqueue(t *testing.T) {
|
||||||
|
pq := NewPriorityQueue[int, int](true) // Min-priority queue
|
||||||
|
pq.Enqueue(3, 3)
|
||||||
|
pq.Enqueue(1, 1)
|
||||||
|
pq.Enqueue(2, 2)
|
||||||
|
|
||||||
|
expected := []int{1, 3, 2} // Expected min-heap state after enqueuing elements
|
||||||
|
for i, v := range pq.heap.elements {
|
||||||
|
if v.Value != expected[i] {
|
||||||
|
t.Errorf("Enqueue() test failed: expected %v at index %d, got %v", expected[i], i, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestDequeue tests the Dequeue method of the PriorityQueue.
|
||||||
|
func TestDequeue(t *testing.T) {
|
||||||
|
pq := NewPriorityQueue[int, int](true)
|
||||||
|
pq.Enqueue(3, 3)
|
||||||
|
pq.Enqueue(1, 1)
|
||||||
|
pq.Enqueue(2, 2)
|
||||||
|
|
||||||
|
if val := pq.Dequeue(); val != 1 {
|
||||||
|
t.Errorf("Dequeue() test failed: expected 1, got %v", val)
|
||||||
|
}
|
||||||
|
|
||||||
|
if val := pq.Dequeue(); val != 2 {
|
||||||
|
t.Errorf("Dequeue() test failed: expected 2, got %v", val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestPeek tests the Peek method of the PriorityQueue.
|
||||||
|
func TestPQueuePeek(t *testing.T) {
|
||||||
|
pq := NewPriorityQueue[int, int](true)
|
||||||
|
pq.Enqueue(3, 3)
|
||||||
|
pq.Enqueue(1, 1)
|
||||||
|
pq.Enqueue(2, 2)
|
||||||
|
|
||||||
|
if val := pq.Peek(); val != 1 {
|
||||||
|
t.Errorf("Peek() test failed: expected 1, got %v", val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if Peek() doesn't remove the element
|
||||||
|
if val := pq.Dequeue(); val != 1 {
|
||||||
|
t.Errorf("Peek() test failed: Peek() should not remove the element")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestIsEmpty tests the IsEmpty method of the PriorityQueue.
|
||||||
|
func TestIsEmpty(t *testing.T) {
|
||||||
|
pq := NewPriorityQueue[int, int](true)
|
||||||
|
|
||||||
|
if !pq.IsEmpty() {
|
||||||
|
t.Errorf("IsEmpty() test failed: expected true, got false")
|
||||||
|
}
|
||||||
|
|
||||||
|
pq.Enqueue(1, 1)
|
||||||
|
if pq.IsEmpty() {
|
||||||
|
t.Errorf("IsEmpty() test failed: expected false, got true")
|
||||||
|
}
|
||||||
|
|
||||||
|
pq.Dequeue()
|
||||||
|
if !pq.IsEmpty() {
|
||||||
|
t.Errorf("IsEmpty() test failed: expected true, got false")
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package math
|
package maths
|
||||||
|
|
||||||
import "math"
|
import "math"
|
||||||
|
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package math
|
package maths
|
||||||
|
|
||||||
import "testing"
|
import "testing"
|
||||||
|
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package math
|
package maths
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package math
|
package maths
|
||||||
|
|
||||||
type Matrix[T comparable] [][]T
|
type Matrix[T comparable] [][]T
|
||||||
|
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package math
|
package maths
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"reflect"
|
"reflect"
|
||||||
Loading…
Reference in New Issue