Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/kneasle/shortlist
An efficient data structure to track the largest items pushed to it.
https://github.com/kneasle/shortlist
data-structures rust rust-library shortlist
Last synced: 11 days ago
JSON representation
An efficient data structure to track the largest items pushed to it.
- Host: GitHub
- URL: https://github.com/kneasle/shortlist
- Owner: kneasle
- License: mit
- Created: 2020-10-09T17:22:47.000Z (over 4 years ago)
- Default Branch: master
- Last Pushed: 2021-06-28T17:24:11.000Z (over 3 years ago)
- Last Synced: 2024-12-16T00:19:58.958Z (about 1 month ago)
- Topics: data-structures, rust, rust-library, shortlist
- Language: Rust
- Homepage:
- Size: 44.9 KB
- Stars: 3
- Watchers: 3
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGE_LOG.md
- License: LICENSE
Awesome Lists containing this project
README
# shortlist
[![Crates.io](https://img.shields.io/crates/v/shortlist.svg)](https://crates.io/crates/shortlist)
[![Docs.rs](https://docs.rs/shortlist/badge.svg)](https://docs.rs/shortlist)A data structure to track the largest items pushed to it with no heap allocations and `O(1)`
amortized time per push.## Features
- Time complexity for pushing is `O(1)` amortized and `O(log n)` worst case (if the inputs are
already sorted)
- No heap allocations except when creating a new `Shortlist`
- 0 dependencies, and only ~150 lines of source code
- No `unsafe`## The Problem
Suppose that you are running a brute force search over a very large search space, but want to
keep more than just the single best item - for example, you want to find the best 100 items out
of a search of a billion options.I.e. you want to implement the following function:
```rust
fn get_best(
big_computation: impl Iterator,
n: usize
) -> Vec {
// Somehow get the `n` largest items produced by `big_computation` ...
}
```## A bad solution
The naive approach to this would be to store every item that we searched. Then once the search
is complete, sort this list and then take however many items we need from the end of the list.
This corresponds to roughly the following code:
```rust
fn get_best(
big_computation: impl Iterator,
n: usize
) -> Vec {
// Collect all the results into a big sorted vec
let mut giant_vec: Vec = big_computation.collect();
giant_vec.sort();
// Return the last and therefore biggest n items with some iterator magic
giant_vec.drain(..).rev().take(n).rev().collect()
}```
But this is massively inefficient in (at least) two ways:
- Sorting very large lists is very slow, and we are sorting potentially billions of items that
we will never need.
- For any decently large search space, storing these items will likely crash the computer by
making it run out of memory.## The solution used by this crate
This is where using a `Shortlist` is useful.A `Shortlist` is a data structure that will dynamically keep a 'shortlist' of the best items
given to it so far, with `O(1)` amortized time for pushing new items. It will also only perform
one heap allocation when the `Shortlist` is created and every subsequent operation will be
allocation free. Therefore, to the user of this library the code becomes:
```rust
use shortlist::Shortlist;fn get_best(
big_computation: impl Iterator,
n: usize
) -> Vec {
// Create a new Shortlist that will take at most `n` items
let mut shortlist = Shortlist::new(n);
// Feed it all the results from `big_computation`
for v in big_computation {
shortlist.push(v);
}
// Return the shortlisted values as a sorted vec
shortlist.into_sorted_vec()
}```
Or as a one-liner:
```rust
use shortlist::Shortlist;fn get_best(big_computation: impl Iterator, n: usize) -> Vec {
Shortlist::from_iter(n, big_computation).into_sorted_vec()
}```
In both cases, the code will make exactly one heap allocation (to reserve space for the
`Shortlist`).License: MIT