Skip to content

Commit bc35214

Browse files
committed
feat: add skip list learning artifacts
1 parent 5a99804 commit bc35214

10 files changed

Lines changed: 248 additions & 0 deletions

Cargo.toml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,18 @@ path = "examples/soluciones/bloom_filter_size_estimate.rs"
145145
name = "solution_bloom_filter_false_positive_probe"
146146
path = "examples/soluciones/bloom_filter_false_positive_probe.rs"
147147

148+
[[example]]
149+
name = "solution_skip_list_trace_levels"
150+
path = "examples/soluciones/skip_list_trace_levels.rs"
151+
152+
[[example]]
153+
name = "solution_skip_list_ordered_index"
154+
path = "examples/soluciones/skip_list_ordered_index.rs"
155+
156+
[[example]]
157+
name = "solution_skip_list_expiring_window"
158+
path = "examples/soluciones/skip_list_expiring_window.rs"
159+
148160
[[bench]]
149161
name = "vector_bench"
150162
path = "benches/vector_bench.rs"
@@ -199,3 +211,8 @@ harness = false
199211
name = "bloom_filter_bench"
200212
path = "benches/bloom_filter_bench.rs"
201213
harness = false
214+
215+
[[bench]]
216+
name = "skip_list_bench"
217+
path = "benches/skip_list_bench.rs"
218+
harness = false

benches/skip_list_bench.rs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
use rust_data_structures::skip_list::SkipList;
2+
use std::collections::BTreeSet;
3+
use std::hint::black_box;
4+
use std::time::{Duration, Instant};
5+
6+
const SIZE: i32 = 20_000;
7+
8+
fn main() {
9+
println!("skip list benchmark (manual, std::time::Instant)");
10+
println!("size: {SIZE}");
11+
println!("ordered insert: {:?}", bench_ordered_insert());
12+
println!("permuted insert: {:?}", bench_permuted_insert());
13+
println!("search: {:?}", bench_search());
14+
println!("iteration: {:?}", bench_iteration());
15+
println!("std btreeset search: {:?}", bench_btreeset_search());
16+
}
17+
18+
fn bench_ordered_insert() -> Duration {
19+
let start = Instant::now();
20+
let mut list = SkipList::with_seed(20, 0.5, 1).unwrap();
21+
for value in 0..SIZE {
22+
black_box(list.insert(value));
23+
}
24+
start.elapsed()
25+
}
26+
27+
fn bench_permuted_insert() -> Duration {
28+
let start = Instant::now();
29+
let mut list = SkipList::with_seed(20, 0.5, 1).unwrap();
30+
for value in permuted_values() {
31+
black_box(list.insert(value));
32+
}
33+
start.elapsed()
34+
}
35+
36+
fn bench_search() -> Duration {
37+
let mut list = SkipList::with_seed(20, 0.5, 1).unwrap();
38+
for value in permuted_values() {
39+
list.insert(value);
40+
}
41+
42+
let start = Instant::now();
43+
for value in 0..SIZE {
44+
black_box(list.contains(&value));
45+
}
46+
start.elapsed()
47+
}
48+
49+
fn bench_iteration() -> Duration {
50+
let mut list = SkipList::with_seed(20, 0.5, 1).unwrap();
51+
for value in permuted_values() {
52+
list.insert(value);
53+
}
54+
55+
let start = Instant::now();
56+
for value in list.iter() {
57+
black_box(value);
58+
}
59+
start.elapsed()
60+
}
61+
62+
fn bench_btreeset_search() -> Duration {
63+
let set = permuted_values().into_iter().collect::<BTreeSet<_>>();
64+
65+
let start = Instant::now();
66+
for value in 0..SIZE {
67+
black_box(set.contains(&value));
68+
}
69+
start.elapsed()
70+
}
71+
72+
fn permuted_values() -> Vec<i32> {
73+
(0..SIZE).map(|value| (value * 37) % SIZE).collect()
74+
}

diagrams/12-skip-list.mmd

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
flowchart TB
2+
title["Skip List: niveles probabilisticos"]
3+
4+
search["buscar 30"]
5+
6+
subgraph level3["Nivel 3"]
7+
h3["head"] --> n30_3["30"] --> end3["nil"]
8+
end
9+
10+
subgraph level2["Nivel 2"]
11+
h2["head"] --> n10_2["10"] --> n30_2["30"] --> end2["nil"]
12+
end
13+
14+
subgraph level1["Nivel 1"]
15+
h1["head"] --> n10_1["10"] --> n20_1["20"] --> n30_1["30"] --> n40_1["40"] --> end1["nil"]
16+
end
17+
18+
search --> h3
19+
h3 --> n30_3
20+
h2 --> n10_2
21+
n10_2 --> n30_2
22+
n10_1 --> n20_1 --> n30_1
23+
24+
insert["insert<br/>elige altura con moneda sesgada"]
25+
remove["remove<br/>actualiza enlaces por nivel"]
26+
iterate["iter<br/>recorre nivel 1"]
27+
expected["busqueda esperada<br/>O(log n)"]
28+
29+
level3 --> expected
30+
level2 --> expected
31+
level1 --> iterate
32+
insert --> level1
33+
remove --> level2

examples/skip_list_advanced.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
use rust_data_structures::skip_list::SkipList;
2+
3+
fn main() {
4+
let mut list = SkipList::with_seed(10, 0.5, 77).unwrap();
5+
6+
for value in [50, 10, 40, 20, 30] {
7+
assert!(list.insert(value));
8+
}
9+
10+
assert!(!list.insert(30));
11+
assert_eq!(list.remove(&10), Some(10));
12+
assert_eq!(list.remove(&99), None);
13+
14+
println!(
15+
"despues de borrar: {:?}",
16+
list.iter().copied().collect::<Vec<_>>()
17+
);
18+
}

examples/skip_list_basic.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
use rust_data_structures::skip_list::SkipList;
2+
3+
fn main() {
4+
let mut index = SkipList::new();
5+
6+
index.insert(30);
7+
index.insert(10);
8+
index.insert(20);
9+
10+
println!("contiene 20: {}", index.contains(&20));
11+
println!("contiene 40: {}", index.contains(&40));
12+
println!("orden: {:?}", index.iter().copied().collect::<Vec<_>>());
13+
}

examples/skip_list_intermediate.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
use rust_data_structures::skip_list::SkipList;
2+
3+
fn main() {
4+
let mut list = SkipList::with_seed(8, 0.5, 2026).unwrap();
5+
6+
for value in [12, 4, 18, 1, 9, 15, 20, 7] {
7+
list.insert(value);
8+
}
9+
10+
println!("len: {}", list.len());
11+
println!("nivel actual: {}", list.current_level());
12+
println!("histograma de alturas: {:?}", list.level_histogram());
13+
}

examples/skip_list_real_case.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
use rust_data_structures::skip_list::SkipList;
2+
3+
fn main() {
4+
let mut pending_jobs = SkipList::with_seed(12, 0.5, 123).unwrap();
5+
6+
for scheduled_at in [1_720_000_300, 1_720_000_100, 1_720_000_200] {
7+
pending_jobs.insert(scheduled_at);
8+
}
9+
10+
let now = 1_720_000_200;
11+
let ready = pending_jobs
12+
.iter()
13+
.copied()
14+
.take_while(|scheduled_at| *scheduled_at <= now)
15+
.collect::<Vec<_>>();
16+
17+
for scheduled_at in ready {
18+
pending_jobs.remove(&scheduled_at);
19+
println!("ejecutar job programado en {scheduled_at}");
20+
}
21+
22+
println!(
23+
"pendientes: {:?}",
24+
pending_jobs.iter().copied().collect::<Vec<_>>()
25+
);
26+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
use rust_data_structures::skip_list::SkipList;
2+
3+
fn main() {
4+
let mut timestamps = SkipList::new();
5+
6+
for timestamp in [10, 40, 20, 50, 30] {
7+
timestamps.insert(timestamp);
8+
}
9+
10+
let expired = timestamps
11+
.iter()
12+
.copied()
13+
.take_while(|timestamp| *timestamp <= 30)
14+
.collect::<Vec<_>>();
15+
16+
for timestamp in expired {
17+
timestamps.remove(&timestamp);
18+
}
19+
20+
assert_eq!(timestamps.iter().copied().collect::<Vec<_>>(), vec![40, 50]);
21+
println!("ventana expirada removida");
22+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
use rust_data_structures::skip_list::SkipList;
2+
3+
fn main() {
4+
let mut offsets = SkipList::new();
5+
6+
for offset in [400, 100, 300, 200] {
7+
assert!(offsets.insert(offset));
8+
}
9+
10+
assert_eq!(
11+
offsets.iter().copied().collect::<Vec<_>>(),
12+
vec![100, 200, 300, 400]
13+
);
14+
assert!(offsets.contains(&300));
15+
16+
println!("indice ordenado listo");
17+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
use rust_data_structures::skip_list::SkipList;
2+
3+
fn main() {
4+
let mut list = SkipList::with_seed(8, 0.5, 2026).unwrap();
5+
6+
for value in 0..16 {
7+
list.insert(value);
8+
}
9+
10+
let histogram = list.level_histogram();
11+
assert_eq!(histogram.iter().sum::<usize>(), list.len());
12+
assert!(list.current_level() <= list.max_level());
13+
14+
println!("histograma de niveles: {histogram:?}");
15+
}

0 commit comments

Comments
 (0)