Skip to content

Commit 0c7744a

Browse files
committed
add heap
1 parent e18c9e5 commit 0c7744a

File tree

5 files changed

+337
-0
lines changed

5 files changed

+337
-0
lines changed

heap/README.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Heap
2+
A generic binary heap (priority queue) implemented in the [MoonBit](https://www.moonbitlang.com/) language.
3+
4+
This implementation supports a **custom comparator**, so you can build either a **min-heap** or **max-heap**, or any domain-specific ordering. It aims to be a clean, robust, and idiomatic template for reuse.
5+
6+
## ✨ Features
7+
- **Generic**: Works with any type `T`.
8+
- **Customizable Ordering**: Provide `(T, T) -> Bool` to define heap priority.
9+
- **Linear Build**: `from_vec` constructs the heap in `O(n)`.
10+
- **Logarithmic Ops**: `add`(push)与 `pop`(extract-top)均为 `O(log n)`.
11+
- **Safe API**: `pop` 返回 `T?`,空堆时安全退出。
12+
13+
## ✅ Requirements
14+
- 使用 MoonBit 动态数组作为底层存储。
15+
- 你需要提供一个比较器 `cmp : (T, T) -> Bool`,其中 `cmp(a, b) = true` 表示 **a 的优先级高于 b**
16+
- 最小堆:`fn(a, b) { a < b }`
17+
- 最大堆:`fn(a, b) { a > b }`
18+
19+
## 🧩 API Overview
20+
```moonbit
21+
pub struct Heap[T] {
22+
items : Array[T]
23+
comparator : (T, T) -> Bool
24+
}
25+
26+
pub fn[T] Heap::new(comparator : (T, T) -> Bool) -> Heap[T]
27+
pub fn[T] Heap::from_vec(items : Array[T], comparator : (T, T) -> Bool) -> Heap[T]
28+
pub fn[T] Heap::build_heap(self : Heap[T]) -> Unit
29+
30+
pub fn[T] Heap::len(self : Heap[T]) -> Int
31+
pub fn[T] Heap::is_empty(self : Heap[T]) -> Bool
32+
pub fn[T] Heap::add(self : Heap[T], value : T) -> Unit // push
33+
pub fn[T] Heap::pop(self : Heap[T]) -> T? // extract top or None
34+
pub fn[T] Heap::iter(self : Heap[T]) -> Iter[T] // internal order (not sorted)
35+

heap/heap.mbt

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
///|
2+
pub struct Heap[T] {
3+
items : Array[T]
4+
comparator : (T, T) -> Bool
5+
}
6+
7+
///|
8+
/// Creates a new, empty heap with a custom comparator function.
9+
///
10+
/// # Parameters
11+
/// - `comparator`: A function that defines the heap's ordering.
12+
///
13+
/// # Returns
14+
/// A new `Heap` instance.
15+
pub fn[T] Heap::new(comparator : (T, T) -> Bool) -> Heap[T] {
16+
Heap::{ items: Array::new(), comparator }
17+
}
18+
19+
///|
20+
/// Creates a heap from a vector and a custom comparator function.
21+
///
22+
/// # Parameters
23+
/// - `items`: A vector of items to be turned into a heap.
24+
/// - `comparator`: A function that defines the heap's ordering.
25+
///
26+
/// # Returns
27+
/// A `Heap` instance with the elements from the provided vector.
28+
pub fn[T] Heap::from_vec(
29+
items : Array[T],
30+
comparator : (T, T) -> Bool,
31+
) -> Heap[T] {
32+
let heap = Heap::{ items, comparator }
33+
heap.build_heap()
34+
heap
35+
}
36+
37+
///|
38+
/// Constructs the heap from an unsorted vector by applying the heapify process.
39+
pub fn[T] Heap::build_heap(self : Heap[T]) -> Unit {
40+
let n = self.items.length()
41+
if n < 2 {
42+
return
43+
}
44+
let mut idx = n / 2 - 1
45+
while true {
46+
self.heapify_down(idx)
47+
if idx == 0 {
48+
break
49+
}
50+
idx -= 1
51+
}
52+
}
53+
54+
///|
55+
/// Returns the number of elements in the heap.
56+
///
57+
/// # Returns
58+
/// The number of elements in the heap.
59+
pub fn[T] Heap::len(self : Heap[T]) -> Int {
60+
self.items.length()
61+
}
62+
63+
///|
64+
/// Checks if the heap is empty.
65+
///
66+
/// # Returns
67+
/// `true` if the heap is empty, `false` otherwise.
68+
pub fn[T] Heap::is_empty(self : Heap[T]) -> Bool {
69+
self.len() == 0
70+
}
71+
72+
///|
73+
/// Adds a new element to the heap and maintains the heap property.
74+
///
75+
/// # Parameters
76+
/// - `value`: The value to add to the heap.
77+
pub fn[T] Heap::add(self : Heap[T], value : T) -> Unit {
78+
self.items.push(value)
79+
self.heapify_up(self.len() - 1)
80+
}
81+
82+
///|
83+
/// Removes and returns the root element from the heap.
84+
///
85+
/// # Returns
86+
/// The root element if the heap is not empty, otherwise `None`.
87+
pub fn[T] Heap::pop(self : Heap[T]) -> T? {
88+
if self.is_empty() {
89+
return None
90+
}
91+
let next = if self.items.length() > 0 {
92+
let val = self.items[0]
93+
let last = self.items.pop().unwrap()
94+
if self.items.length() > 0 {
95+
self.items[0] = last
96+
}
97+
Some(val)
98+
} else {
99+
None
100+
}
101+
if !self.is_empty() {
102+
self.heapify_down(0)
103+
}
104+
next
105+
}
106+
107+
///|
108+
/// Returns an iterator over the elements in the heap.
109+
///
110+
/// # Returns
111+
/// An iterator over the elements in the heap, in their internal order.
112+
pub fn[T] Heap::iter(self : Heap[T]) -> Iter[T] {
113+
self.items.iter()
114+
}
115+
116+
///|
117+
/// Moves an element upwards to restore the heap property.
118+
///
119+
/// # Parameters
120+
/// - `idx`: The index of the element to heapify up.
121+
fn[T] Heap::heapify_up(self : Heap[T], idx : Int) -> Unit {
122+
let mut idx0 = idx
123+
while self.parent_idx(idx0) is Some(pdx) { // while-let 等价写法
124+
if (self.comparator)(self.items[idx0], self.items[pdx]) {
125+
let tmp = self.items[idx0]
126+
self.items[idx0] = self.items[pdx]
127+
self.items[pdx] = tmp
128+
idx0 = pdx
129+
} else {
130+
break
131+
}
132+
}
133+
}
134+
135+
///|
136+
/// Moves an element downwards to restore the heap property.
137+
///
138+
/// # Parameters
139+
/// - `idx`: The index of the element to heapify down.
140+
fn[T] Heap::heapify_down(self : Heap[T], idx : Int) -> Unit {
141+
let mut idx0 = idx
142+
while self.children_present(idx0) {
143+
let cdx = if self.right_child_idx(idx0) >= self.len() {
144+
self.left_child_idx(idx0)
145+
} else {
146+
let ldx = self.left_child_idx(idx0)
147+
let rdx = self.right_child_idx(idx0)
148+
if (self.comparator)(self.items[ldx], self.items[rdx]) {
149+
ldx
150+
} else {
151+
rdx
152+
}
153+
}
154+
if (self.comparator)(self.items[cdx], self.items[idx0]) {
155+
self.items.swap(idx0, cdx)
156+
idx0 = cdx
157+
} else {
158+
break
159+
}
160+
}
161+
}
162+
163+
///|
164+
/// Returns the index of the parent of the element at `idx`.
165+
///
166+
/// # Parameters
167+
/// - `idx`: The index of the element.
168+
///
169+
/// # Returns
170+
/// The index of the parent element if it exists, otherwise `None`.
171+
fn[T] Heap::parent_idx(self : Heap[T], idx : Int) -> Int? {
172+
if idx > 0 {
173+
Some((idx - 1) / 2)
174+
} else {
175+
None
176+
}
177+
}
178+
179+
///|
180+
/// Checks if the element at `idx` has children.
181+
///
182+
/// # Parameters
183+
/// - `idx`: The index of the element.
184+
///
185+
/// # Returns
186+
/// `true` if the element has children, `false` otherwise.
187+
fn[T] Heap::children_present(self : Heap[T], idx : Int) -> Bool {
188+
self.left_child_idx(idx) < self.len()
189+
}
190+
191+
///|
192+
/// Returns the index of the left child of the element at `idx`.
193+
///
194+
/// # Parameters
195+
/// - `idx`: The index of the element.
196+
///
197+
/// # Returns
198+
/// The index of the left child.
199+
fn[T] Heap::left_child_idx(self : Heap[T], idx : Int) -> Int {
200+
idx * 2 + 1
201+
}
202+
203+
///|
204+
/// Returns the index of the right child of the element at `idx`.
205+
///
206+
/// # Parameters
207+
/// - `idx`: The index of the element.
208+
///
209+
/// # Returns
210+
/// The index of the right child.
211+
fn[T] Heap::right_child_idx(self : Heap[T], idx : Int) -> Int {
212+
self.left_child_idx(idx) + 1
213+
}

heap/heap.mbti

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Generated using `moon info`, DON'T EDIT IT
2+
package "Lampese/Algorithms-Moonbit/heap"
3+
4+
// Values
5+
6+
// Errors
7+
8+
// Types and methods
9+
pub struct Heap[T] {
10+
items : Array[T]
11+
comparator : (T, T) -> Bool
12+
}
13+
fn[T] Heap::add(Self[T], T) -> Unit
14+
fn[T] Heap::build_heap(Self[T]) -> Unit
15+
fn[T] Heap::from_vec(Array[T], (T, T) -> Bool) -> Self[T]
16+
fn[T] Heap::is_empty(Self[T]) -> Bool
17+
fn[T] Heap::iter(Self[T]) -> Iter[T]
18+
fn[T] Heap::len(Self[T]) -> Int
19+
fn[T] Heap::new((T, T) -> Bool) -> Self[T]
20+
fn[T] Heap::pop(Self[T]) -> T?
21+
22+
// Type aliases
23+
24+
// Traits
25+

heap/heap_test.mbt

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
///|
2+
test "heap_empty_test" {
3+
let heap : Heap[Int] = Heap::new(fn(x, y) { true })
4+
inspect(heap.pop(), content="None")
5+
}
6+
7+
///|
8+
test "heap_max_test" {
9+
let heap = Heap::new(fn(x, y) { if x > y { true } else { false } })
10+
heap.add(4)
11+
heap.add(2)
12+
heap.add(9)
13+
heap.add(11)
14+
assert_eq(heap.len(), 4)
15+
assert_eq(heap.pop(), Some(11))
16+
assert_eq(heap.pop(), Some(9))
17+
assert_eq(heap.pop(), Some(4))
18+
heap.add(1)
19+
assert_eq(heap.pop(), Some(2))
20+
assert_eq(heap.pop(), Some(1))
21+
assert_eq(heap.pop(), None)
22+
}
23+
24+
///|
25+
test "heap_min_test" {
26+
let heap = Heap::new(fn(x, y) { if x < y { true } else { false } })
27+
heap.add(4)
28+
heap.add(2)
29+
heap.add(9)
30+
heap.add(11)
31+
assert_eq(heap.len(), 4)
32+
assert_eq(heap.pop(), Some(2))
33+
assert_eq(heap.pop(), Some(4))
34+
assert_eq(heap.pop(), Some(9))
35+
heap.add(1)
36+
assert_eq(heap.pop(), Some(1))
37+
assert_eq(heap.pop(), Some(11))
38+
assert_eq(heap.pop(), None)
39+
}
40+
41+
///|
42+
test "heap_min_from_vec_test" {
43+
let vec = [3, 1, 4, 1, 5, 9, 2, 6, 5]
44+
let heap = Heap::from_vec(vec, fn(x, y) { if x < y { true } else { false } })
45+
assert_eq(heap.len(), 9)
46+
assert_eq(heap.pop(), Some(1))
47+
assert_eq(heap.pop(), Some(1))
48+
assert_eq(heap.pop(), Some(2))
49+
heap.add(0)
50+
assert_eq(heap.pop(), Some(0))
51+
}
52+
53+
///|
54+
test "heap_max_from_vec_test" {
55+
let vec = [3, 1, 4, 1, 5, 9, 2, 6, 5]
56+
let heap = Heap::from_vec(vec, fn(x, y) { if x > y { true } else { false } })
57+
assert_eq(heap.len(), 9)
58+
assert_eq(heap.pop(), Some(9))
59+
assert_eq(heap.pop(), Some(6))
60+
assert_eq(heap.pop(), Some(5))
61+
heap.add(10)
62+
assert_eq(heap.pop(), Some(10))
63+
}

heap/moon.pkg.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{ }

0 commit comments

Comments
 (0)