diff --git a/go.mod b/go.mod index db7c2c3..13196b8 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,10 @@ module github.com/fotonmoton/algorithms go 1.18 + +require ( + github.com/davecgh/go-spew v1.1.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/stretchr/testify v1.7.0 // indirect + gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..b380ae4 --- /dev/null +++ b/go.sum @@ -0,0 +1,10 @@ +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/sorting/priority_queue.go b/sorting/priority_queue.go new file mode 100644 index 0000000..42c21e7 --- /dev/null +++ b/sorting/priority_queue.go @@ -0,0 +1,93 @@ +package sorting + +type PriorityQueue[T any] interface { + top() T + insert(T) + delete() T + size() int + isEmpty() bool +} + +type priorityQueue[T any] struct { + n int + heap []T + less func(T, T) bool +} + +func NewPQ[T any](less func(T, T) bool) PriorityQueue[T] { + return &priorityQueue[T]{ + n: 0, + less: less, + // First element is not used + // to make equation "next = current * 2" work + heap: make([]T, 1, 1), + } +} + +func (pq *priorityQueue[T]) top() T { + // After all operations biggest element + // should be always at the beginning of the array + return pq.heap[1] +} + +func (pq *priorityQueue[T]) insert(item T) { + // TODO: increase by square when capacity is full + pq.heap = append(pq.heap, item) + pq.n++ + pq.swim(pq.n) +} + +func (pq *priorityQueue[T]) delete() T { + top := pq.top() + pq.swap(1, pq.n) + pq.heap = pq.heap[:pq.n] + pq.n-- + pq.sink(1) + return top +} + +func (pq *priorityQueue[_]) size() int { + return pq.n +} + +func (pq *priorityQueue[_]) isEmpty() bool { + return pq.n == 0 +} + +func (pq *priorityQueue[_]) swap(i, j int) { + pq.heap[i], pq.heap[j] = pq.heap[j], pq.heap[i] +} + +func (pq *priorityQueue[T]) swim(start int) { + for k := start; k > 1 && pq.less(pq.heap[k/2], pq.heap[k]); k = k / 2 { + pq.swap(k/2, k) + } +} + +func (pq *priorityQueue[T]) sink(k int) { + // While k is a parent with some children + for 2*k <= pq.n { + + // First child of a parent k + j := 2 * k + + // If first child is less than second + // we choose second one for exchange. + // Parent should be swapped with biggest child + if j < pq.n && pq.less(pq.heap[j], pq.heap[j+1]) { + j++ + } + + // If parent is already bigger than biggest child + // we found the position + if !pq.less(pq.heap[k], pq.heap[j]) { + break + } + + // swap parent node with child + pq.swap(j, k) + + // child node is a new parent + k = j + } +} diff --git a/sorting/priority_queue_test.go b/sorting/priority_queue_test.go new file mode 100644 index 0000000..ee7d43a --- /dev/null +++ b/sorting/priority_queue_test.go @@ -0,0 +1,59 @@ +package sorting + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +var intCompare = func(t1, t2 int) bool { return t1 < t2 } + +func TestNew(t *testing.T) { + pq := NewPQ(intCompare) + assert.NotNil(t, pq) +} + +func TestSize(t *testing.T) { + pq := NewPQ(intCompare) + assert.Equal(t, 0, pq.size()) + pq.insert(1) + assert.Equal(t, 1, pq.size()) + pq.delete() + assert.Equal(t, 0, pq.size()) + +} + +func TestIsEmpty(t *testing.T) { + pq := NewPQ(intCompare) + assert.Equal(t, true, pq.isEmpty()) + pq.insert(1) + assert.Equal(t, false, pq.isEmpty()) +} + +func TestInsert(t *testing.T) { + pq := NewPQ(intCompare) + pq.insert(1) + assert.Equal(t, 1, pq.top()) + pq.insert(4) + assert.Equal(t, 4, pq.top()) + pq.insert(3) + assert.Equal(t, 4, pq.top()) + pq.insert(5) + assert.Equal(t, 5, pq.top()) +} + +func TestDelete(t *testing.T) { + pq := NewPQ(intCompare) + pq.insert(1) + pq.insert(2) + pq.insert(6) + pq.insert(5) + pq.insert(4) + pq.insert(3) + assert.Equal(t, 6, pq.delete()) + assert.Equal(t, 5, pq.delete()) + assert.Equal(t, 4, pq.delete()) + assert.Equal(t, 3, pq.delete()) + assert.Equal(t, 2, pq.delete()) + assert.Equal(t, 1, pq.delete()) +}