Skip to content

Commit 9ed99b3

Browse files
committed
Add leftist heap
1 parent f5ed236 commit 9ed99b3

File tree

4 files changed

+145
-0
lines changed

4 files changed

+145
-0
lines changed

leftist-heap/Cargo.lock

+7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

leftist-heap/Cargo.toml

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[package]
2+
name = "leftist-heap"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[dependencies]

leftist-heap/src/leftist_heap.rs

+116
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
use std::{cmp::min, mem::swap};
2+
3+
struct Node<V: Ord> {
4+
value: V,
5+
left: Option<Box<Node<V>>>,
6+
right: Option<Box<Node<V>>>,
7+
rank: usize,
8+
}
9+
10+
impl<V: Ord> Node<V> {
11+
// 単一のノードからなるヒープ(の根)を返す。
12+
fn singleton(value: V) -> Option<Box<Node<V>>> {
13+
Some(Box::new(Node {
14+
value,
15+
left: None,
16+
right: None,
17+
rank: 1,
18+
}))
19+
}
20+
21+
// a, b をそれぞれ根とするふたつのヒープをマージする。
22+
// O(log n)
23+
fn merge(a: Option<Box<Node<V>>>, b: Option<Box<Node<V>>>) -> Option<Box<Node<V>>> {
24+
match (a, b) {
25+
(None, b) => b,
26+
(a, None) => a,
27+
(Some(a), Some(b)) => {
28+
// a のほうが値が小さくなるように変数を置きなおす。
29+
// すると a がマージ後の木の親となるノードとなる。
30+
let (mut a, b) = if a.value < b.value { (a, b) } else { (b, a) };
31+
// a.right に b をマージする。
32+
a.right = Self::merge(a.right, Some(b));
33+
// leftist property が破れていたら swap して修復する。
34+
// leftist property:
35+
// 任意のノード x について x.left.rank ≧ x.right.rank
36+
let rank_l = a.left.as_ref().map_or(0, |l| l.rank);
37+
let rank_r = a.right.as_ref().map_or(0, |r| r.rank);
38+
if rank_l < rank_r {
39+
swap(&mut a.left, &mut a.right);
40+
}
41+
// ランクを更新する。
42+
a.rank = min(rank_l, rank_r) + 1;
43+
Some(a)
44+
}
45+
}
46+
}
47+
}
48+
49+
// leftist heap によって実装された優先度付きキュー。
50+
pub struct LeftistHeap<V: Ord> {
51+
root: Option<Box<Node<V>>>,
52+
}
53+
54+
impl<V: Ord> LeftistHeap<V> {
55+
// 空のヒープを作成する。
56+
// O(1)
57+
pub fn new() -> Self {
58+
Self { root: None }
59+
}
60+
61+
// 空であれば真を返す。
62+
// O(1)
63+
pub fn is_empty(&self) -> bool {
64+
self.root.is_none()
65+
}
66+
67+
// ヒープに value を追加する。
68+
// O(log n)
69+
pub fn push(&mut self, value: V) {
70+
self.root = Node::merge(self.root.take(), Node::singleton(value))
71+
}
72+
73+
// ヒープから最小の値を取り除いて、それを返す。
74+
// O(log n)
75+
pub fn pop_min(&mut self) -> Option<V> {
76+
let Some(root) = self.root.take() else {
77+
return None;
78+
};
79+
let value = root.value;
80+
self.root = Node::merge(root.left, root.right);
81+
Some(value)
82+
}
83+
84+
// ふたつのヒープをマージする。
85+
// O(log n)
86+
pub fn merge(a: LeftistHeap<V>, b: LeftistHeap<V>) -> LeftistHeap<V> {
87+
LeftistHeap {
88+
root: Node::merge(a.root, b.root),
89+
}
90+
}
91+
}
92+
93+
#[cfg(test)]
94+
mod tests {
95+
use super::LeftistHeap;
96+
97+
#[test]
98+
fn basic_test() {
99+
let mut heap = LeftistHeap::new();
100+
101+
// push
102+
for v in [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5] {
103+
heap.push(v)
104+
}
105+
106+
// pop_min
107+
let mut got = Vec::new();
108+
while !heap.is_empty() {
109+
got.push(heap.pop_min().unwrap());
110+
}
111+
112+
// verify
113+
let want = vec![1, 1, 2, 3, 3, 4, 5, 5, 5, 6, 9];
114+
assert_eq!(want, got);
115+
}
116+
}

leftist-heap/src/main.rs

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
mod leftist_heap;
2+
use leftist_heap::LeftistHeap;
3+
4+
fn main() {
5+
let mut heap = LeftistHeap::new();
6+
7+
// push
8+
for v in [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5] {
9+
heap.push(v)
10+
}
11+
12+
// pop_min
13+
while !heap.is_empty() {
14+
println!("{:?}", heap.pop_min());
15+
}
16+
}

0 commit comments

Comments
 (0)