aboutsummaryrefslogtreecommitdiff
path: root/internal/priorityqueue
diff options
context:
space:
mode:
Diffstat (limited to 'internal/priorityqueue')
-rw-r--r--internal/priorityqueue/len.go6
-rw-r--r--internal/priorityqueue/new.go6
-rw-r--r--internal/priorityqueue/pop.go21
-rw-r--r--internal/priorityqueue/push.go7
-rw-r--r--internal/priorityqueue/queue0
-rw-r--r--internal/priorityqueue/queue.go7
-rw-r--r--internal/priorityqueue/queue_test.go35
-rw-r--r--internal/priorityqueue/sift_down.go23
-rw-r--r--internal/priorityqueue/sift_up.go13
9 files changed, 118 insertions, 0 deletions
diff --git a/internal/priorityqueue/len.go b/internal/priorityqueue/len.go
new file mode 100644
index 00000000..aca8e0ce
--- /dev/null
+++ b/internal/priorityqueue/len.go
@@ -0,0 +1,6 @@
+package priorityqueue
+
+// Len reports the number of queued items.
+func (queue *Queue[T]) Len() int {
+ return len(queue.items)
+}
diff --git a/internal/priorityqueue/new.go b/internal/priorityqueue/new.go
new file mode 100644
index 00000000..bf1c1819
--- /dev/null
+++ b/internal/priorityqueue/new.go
@@ -0,0 +1,6 @@
+package priorityqueue
+
+// New builds one empty priority queue ordered by less.
+func New[T any](less func(left, right T) bool) *Queue[T] {
+ return &Queue[T]{less: less}
+}
diff --git a/internal/priorityqueue/pop.go b/internal/priorityqueue/pop.go
new file mode 100644
index 00000000..2190b065
--- /dev/null
+++ b/internal/priorityqueue/pop.go
@@ -0,0 +1,21 @@
+package priorityqueue
+
+// Pop removes one highest-priority item.
+func (queue *Queue[T]) Pop() (T, bool) {
+ if len(queue.items) == 0 {
+ var zero T
+
+ return zero, false
+ }
+
+ last := len(queue.items) - 1
+ top := queue.items[0]
+ queue.items[0] = queue.items[last]
+ queue.items = queue.items[:last]
+
+ if len(queue.items) > 0 {
+ queue.siftDown(0)
+ }
+
+ return top, true
+}
diff --git a/internal/priorityqueue/push.go b/internal/priorityqueue/push.go
new file mode 100644
index 00000000..d0c6cadd
--- /dev/null
+++ b/internal/priorityqueue/push.go
@@ -0,0 +1,7 @@
+package priorityqueue
+
+// Push inserts one item.
+func (queue *Queue[T]) Push(item T) {
+ queue.items = append(queue.items, item)
+ queue.siftUp(len(queue.items) - 1)
+}
diff --git a/internal/priorityqueue/queue b/internal/priorityqueue/queue
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/internal/priorityqueue/queue
diff --git a/internal/priorityqueue/queue.go b/internal/priorityqueue/queue.go
new file mode 100644
index 00000000..279a400f
--- /dev/null
+++ b/internal/priorityqueue/queue.go
@@ -0,0 +1,7 @@
+package priorityqueue
+
+// Queue is one slice-backed priority queue.
+type Queue[T any] struct {
+ items []T
+ less func(left, right T) bool
+}
diff --git a/internal/priorityqueue/queue_test.go b/internal/priorityqueue/queue_test.go
new file mode 100644
index 00000000..acb4f3e8
--- /dev/null
+++ b/internal/priorityqueue/queue_test.go
@@ -0,0 +1,35 @@
+package priorityqueue_test
+
+import (
+ "slices"
+ "testing"
+
+ "codeberg.org/lindenii/furgit/internal/priorityqueue"
+)
+
+func TestQueueAscending(t *testing.T) {
+ t.Parallel()
+
+ queue := priorityqueue.New(func(left, right int) bool {
+ return left < right
+ })
+
+ for _, value := range []int{5, 1, 4, 3, 2} {
+ queue.Push(value)
+ }
+
+ var got []int
+ for queue.Len() > 0 {
+ value, ok := queue.Pop()
+ if !ok {
+ t.Fatal("Pop should succeed")
+ }
+
+ got = append(got, value)
+ }
+
+ want := []int{1, 2, 3, 4, 5}
+ if !slices.Equal(got, want) {
+ t.Fatalf("pop order = %v, want %v", got, want)
+ }
+}
diff --git a/internal/priorityqueue/sift_down.go b/internal/priorityqueue/sift_down.go
new file mode 100644
index 00000000..83e3fd40
--- /dev/null
+++ b/internal/priorityqueue/sift_down.go
@@ -0,0 +1,23 @@
+package priorityqueue
+
+func (queue *Queue[T]) siftDown(idx int) {
+ for {
+ left := idx*2 + 1
+ if left >= len(queue.items) {
+ return
+ }
+
+ best := left
+ right := left + 1
+ if right < len(queue.items) && queue.less(queue.items[right], queue.items[left]) {
+ best = right
+ }
+
+ if !queue.less(queue.items[best], queue.items[idx]) {
+ return
+ }
+
+ queue.items[idx], queue.items[best] = queue.items[best], queue.items[idx]
+ idx = best
+ }
+}
diff --git a/internal/priorityqueue/sift_up.go b/internal/priorityqueue/sift_up.go
new file mode 100644
index 00000000..7ff4453f
--- /dev/null
+++ b/internal/priorityqueue/sift_up.go
@@ -0,0 +1,13 @@
+package priorityqueue
+
+func (queue *Queue[T]) siftUp(idx int) {
+ for idx > 0 {
+ parent := (idx - 1) / 2
+ if !queue.less(queue.items[idx], queue.items[parent]) {
+ return
+ }
+
+ queue.items[idx], queue.items[parent] = queue.items[parent], queue.items[idx]
+ idx = parent
+ }
+}