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"
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
package math
|
||||
package maths
|
||||
|
||||
import "testing"
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
package math
|
||||
package maths
|
||||
|
||||
import (
|
||||
"testing"
|
||||
@ -1,4 +1,4 @@
|
||||
package math
|
||||
package maths
|
||||
|
||||
type Matrix[T comparable] [][]T
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
package math
|
||||
package maths
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
Loading…
Reference in New Issue