Skip to content

Commit 14ec86b

Browse files
committed
Add skew-heap
1 parent 9ed99b3 commit 14ec86b

File tree

6 files changed

+310
-0
lines changed

6 files changed

+310
-0
lines changed

skew-heap-cpp/skew_heap.hpp

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
#pragma once
2+
#include <memory>
3+
4+
namespace skewheap {
5+
6+
template<typename T>
7+
struct Node {
8+
T x;
9+
Node* l;
10+
Node* r;
11+
Node* parent;
12+
13+
static Node<T>* empty() {
14+
return nullptr;
15+
}
16+
17+
static Node<T>* singleton(T x) {
18+
return new Node<T>{x, nullptr, nullptr};
19+
}
20+
21+
static Node<T>* meld(Node<T>* a, Node<T>* b, Node<T>* parent) {
22+
if (a == nullptr && b == nullptr) {
23+
return nullptr;
24+
}
25+
if (a == nullptr) {
26+
b->parent = parent;
27+
return b;
28+
}
29+
if (b == nullptr) {
30+
a->parent = parent;
31+
return a;
32+
}
33+
if (a->x > b->x) std::swap(a, b);
34+
a->r = meld(a->r, b, a);
35+
std::swap(a->l, a->r);
36+
a->parent = parent;
37+
return a;
38+
}
39+
40+
Node* push(T x) {
41+
return meld(this, singleton(x));
42+
}
43+
44+
T pop() {
45+
meld(l, r);
46+
}
47+
};
48+
49+
} // namespace skewheap

skew-heap/Cargo.lock

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

skew-heap/Cargo.toml

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

skew-heap/src/intrusive_skew_heap.rs

+164
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
use std::{
2+
mem::{offset_of, swap},
3+
ptr::null_mut,
4+
};
5+
6+
#[derive(Debug)]
7+
pub struct Hook {
8+
parent: *mut Hook,
9+
left: *mut Hook,
10+
right: *mut Hook,
11+
}
12+
13+
#[derive(Debug)]
14+
pub struct Entry {
15+
x: i64,
16+
17+
parent: *mut Entry,
18+
left: *mut Entry,
19+
right: *mut Entry,
20+
}
21+
22+
impl Entry {
23+
pub fn new(x: i64) -> Entry {
24+
Entry {
25+
x,
26+
parent: std::ptr::null_mut(),
27+
left: std::ptr::null_mut(),
28+
right: std::ptr::null_mut(),
29+
}
30+
}
31+
}
32+
33+
pub fn meld(mut a: *mut Entry, mut b: *mut Entry, parent: *mut Entry) -> *mut Entry {
34+
if a.is_null() {
35+
if b.is_null() {
36+
return null_mut();
37+
}
38+
unsafe { (*b).parent = parent };
39+
return b;
40+
}
41+
if b.is_null() {
42+
unsafe { (*a).parent = parent };
43+
return a;
44+
}
45+
unsafe {
46+
if (*a).x > (*b).x {
47+
swap(&mut a, &mut b);
48+
}
49+
(*a).right = meld((*a).right, b, a);
50+
swap(&mut (*a).left, &mut (*a).right);
51+
(*a).parent = parent;
52+
}
53+
a
54+
}
55+
56+
pub unsafe fn meld_hook<Entry: Ord, const OFFSET: usize>(
57+
mut a: *mut Hook,
58+
mut b: *mut Hook,
59+
parent: *mut Hook,
60+
) -> *mut Hook {
61+
if a.is_null() {
62+
if b.is_null() {
63+
return null_mut();
64+
}
65+
unsafe { (*b).parent = parent };
66+
return b;
67+
}
68+
if b.is_null() {
69+
unsafe { (*a).parent = parent };
70+
return a;
71+
}
72+
unsafe {
73+
let entry_a = a.byte_sub(OFFSET) as *const Entry;
74+
let entry_b = b.byte_sub(OFFSET) as *const Entry;
75+
if entry_a > entry_b {
76+
swap(&mut a, &mut b);
77+
}
78+
(*a).right = meld_hook::<Entry, OFFSET>((*a).right, b, a);
79+
swap(&mut (*a).left, &mut (*a).right);
80+
(*a).parent = parent;
81+
}
82+
a
83+
}
84+
85+
// Pushes a new element into the heap and returns the new root
86+
pub fn push(root: *mut Entry, new_entry: *mut Entry) -> *mut Entry {
87+
meld(root, new_entry, null_mut())
88+
}
89+
90+
// Returns the new root and the removed minimum element
91+
pub fn pop_min(root: *mut Entry) -> (*mut Entry, *mut Entry) {
92+
if root.is_null() {
93+
return (null_mut(), null_mut());
94+
}
95+
let new_root = meld(
96+
unsafe { (*root).left },
97+
unsafe { (*root).right },
98+
null_mut(),
99+
);
100+
unsafe {
101+
(*root).left = null_mut();
102+
(*root).right = null_mut();
103+
(*root).parent = null_mut();
104+
}
105+
(new_root, root)
106+
}
107+
108+
// Unlinks the entry from the heap and returns new root
109+
pub fn unlink(root: *mut Entry, entry: *mut Entry) -> *mut Entry {
110+
// If the entry is the root, just pop it
111+
if entry == root {
112+
let (new_root, _) = pop_min(root);
113+
return new_root;
114+
}
115+
assert_ne!(unsafe { (*entry).parent }, null_mut());
116+
117+
let subtree = meld(
118+
unsafe { (*entry).left },
119+
unsafe { (*entry).right },
120+
unsafe { (*entry).parent },
121+
);
122+
// Replace the link from the parent node to the entry with the subtree
123+
unsafe {
124+
if (*(*entry).parent).left == entry {
125+
(*(*entry).parent).left = subtree;
126+
} else {
127+
(*(*entry).parent).right = subtree;
128+
}
129+
}
130+
// Clear the links from the entry
131+
unsafe {
132+
(*entry).left = null_mut();
133+
(*entry).right = null_mut();
134+
(*entry).parent = null_mut();
135+
}
136+
// The root of the heap does not change, so return it as is
137+
root
138+
}
139+
140+
#[cfg(test)]
141+
mod tests {
142+
#[test]
143+
fn test_skew_heap() {
144+
use super::{pop_min, push, unlink, Entry};
145+
let mut root = std::ptr::null_mut();
146+
let mut entries = vec![];
147+
for i in 0..10 {
148+
let mut entry = Box::new(Entry::new(i));
149+
root = push(root, entry.as_mut() as *mut Entry);
150+
entries.push(entry);
151+
}
152+
/*
153+
for entry in entries.iter() {
154+
let (new_root, min_entry) = pop_min(root);
155+
assert_eq!(unsafe { (*min_entry).x }, unsafe { (**entry).x });
156+
root = new_root;
157+
}
158+
*/
159+
for mut entry in entries {
160+
root = unlink(root, entry.as_mut() as *mut Entry);
161+
}
162+
assert!(root.is_null());
163+
}
164+
}

skew-heap/src/main.rs

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
pub mod skewheap;
2+
pub mod intrusive_skew_heap;
3+
4+
fn main() {
5+
println!("Hello, world!");
6+
}

skew-heap/src/skewheap.rs

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
use std::mem::swap;
2+
3+
pub enum SkewHeap<T: Ord> {
4+
Empty,
5+
Node(SkewNode<T>),
6+
}
7+
8+
pub struct SkewNode<T: Ord> {
9+
x: T,
10+
l: Box<SkewHeap<T>>,
11+
r: Box<SkewHeap<T>>,
12+
}
13+
14+
impl<T: Ord> SkewHeap<T> {
15+
pub fn singleton(x: T) -> SkewHeap<T> {
16+
Self::Node(SkewNode {
17+
x,
18+
l: Box::new(Self::Empty),
19+
r: Box::new(Self::Empty),
20+
})
21+
}
22+
23+
pub fn is_empty(&self) -> bool {
24+
match self {
25+
Self::Empty => true,
26+
_ => false,
27+
}
28+
}
29+
30+
pub fn push(self, x: T) -> SkewHeap<T> {
31+
Self::merge(self, Self::singleton(x))
32+
}
33+
34+
pub fn pop_min(self) -> (Option<T>, SkewHeap<T>) {
35+
match self {
36+
Self::Empty => (None, self),
37+
Self::Node(node) => (Some(node.x), Self::merge(*node.l, *node.r)),
38+
}
39+
}
40+
41+
pub fn merge(a: SkewHeap<T>, b: SkewHeap<T>) -> SkewHeap<T> {
42+
use SkewHeap::*;
43+
match (a, b) {
44+
(Empty, b) => b,
45+
(a, Empty) => a,
46+
(Node(mut a), Node(mut b)) => {
47+
if a.x > b.x {
48+
swap(&mut a, &mut b);
49+
}
50+
a.r = Box::new(Self::merge(*a.r, Node(b)));
51+
swap(&mut a.l, &mut a.r);
52+
Node(a)
53+
}
54+
}
55+
}
56+
}
57+
58+
#[cfg(test)]
59+
mod tests {
60+
#[test]
61+
fn test_skew_heap() {
62+
use super::SkewHeap;
63+
let mut heap = SkewHeap::Empty;
64+
for x in [1, 1, 2, 2, 3, 3, 3, 3, 4, 4, 5, 5, 5, 6, 6, 7, 8, 8, 9, 9, 9] {
65+
heap = heap.push(x);
66+
}
67+
let mut v = Vec::new();
68+
while !heap.is_empty() {
69+
let (x, h) = heap.pop_min();
70+
let Some(x) = x else {
71+
panic!("pop_min should return Some(x)");
72+
};
73+
heap = h;
74+
v.push(x);
75+
}
76+
assert_eq!(v, vec![1, 1, 2, 2, 3, 3, 3, 3, 4, 4, 5, 5, 5, 6, 6, 7, 8, 8, 9, 9, 9]);
77+
}
78+
}

0 commit comments

Comments
 (0)