#priority #queue #heap

no-std priority-queue

实现为一个堆的优先队列,具有一个高效更改项目优先级的函数

43 个版本 (21 个稳定版)

2.1.0 2024年8月11日
2.0.3 2024年5月25日
2.0.2 2024年2月28日
1.4.0 2024年2月4日
0.2.0 2017年7月24日

#7数据结构

Download history 50731/week @ 2024-05-03 55634/week @ 2024-05-10 53978/week @ 2024-05-17 72794/week @ 2024-05-24 78379/week @ 2024-05-31 77707/week @ 2024-06-07 67258/week @ 2024-06-14 75322/week @ 2024-06-21 70193/week @ 2024-06-28 83178/week @ 2024-07-05 73832/week @ 2024-07-12 81863/week @ 2024-07-19 77150/week @ 2024-07-26 55549/week @ 2024-08-02 51899/week @ 2024-08-09 42362/week @ 2024-08-16

每月243,612 次下载
400 个crate中使用 (84直接使用)

LGPL-3.0-or-later OR MPL-2.0

115KB
2K SLoC

PriorityQueue

crate Build Test

此crate实现了一个具有更改对象优先级功能的优先队列。优先级和项目存储在IndexMap中,队列作为索引堆实现。

请在此处阅读API 文档

使用方法

要使用此crate,只需将以下字符串添加到您的Cargo.toml

priority-queue = "2.0.0"

或使用以下命令cargo add priority-queue

版本号遵循semver约定。

然后在您的Rust源代码中使用数据结构,如下例所示。

请记住,如果您需要serde支持,应使用--features serde进行编译。

示例

use priority_queue::PriorityQueue;

fn main() {
    let mut pq = PriorityQueue::new();

    assert!(pq.is_empty());
    pq.push("Apples", 5);
    pq.push("Bananas", 8);
    pq.push("Strawberries", 23);

    assert_eq!(pq.peek(), Some((&"Strawberries", &23)));

    for (item, _) in pq.into_sorted_iter() {
        println!("{}", item);
    }
}

默认情况下,将首先提取优先级最高的元素。可以使用标准包装器轻松反转顺序Reverse<T>

use priority_queue::PriorityQueue;
use std::cmp::Reverse;

fn main() {
    let mut pq = PriorityQueue::new();

    assert!(pq.is_empty());
    pq.push("Apples", Reverse(5));
    pq.push("Bananas", Reverse(8));
    pq.push("Strawberries", Reverse(23));

    assert_eq!(pq.peek(), Some((&"Apples", &Reverse(5))));

    for (item, _) in pq.into_sorted_iter() {
        println!("{}", item);
    }
}

加速

您可以使用自定义BuildHasher对底层的IndexMap进行操作,从而获得更好的性能。例如,您可以使用快速的FxHash散列器创建队列

use hashbrown::hash_map::DefaultHashBuilder;

let mut pq = PriorityQueue::<_, _, DefaultHashBuilder>::with_default_hasher();

注意:FxHash不提供任何针对dos攻击的保护。这意味着某些病理输入可能会使hashmap的操作达到O(n^2)。如果无法控制输入,请使用标准散列器。

基准测试

已运行一些基准测试,以比较此优先队列与标准BinaryHeap的性能,同时使用FxHash散列器。在Ryzen 9 3900X上,基准测试产生了以下结果

test benchmarks::priority_change_on_large_double_queue     ... bench:          25 ns/iter (+/- 1)
test benchmarks::priority_change_on_large_double_queue_fx  ... bench:          21 ns/iter (+/- 1)
test benchmarks::priority_change_on_large_queue            ... bench:          15 ns/iter (+/- 0)
test benchmarks::priority_change_on_large_queue_fx         ... bench:          11 ns/iter (+/- 0)
test benchmarks::priority_change_on_large_queue_std        ... bench:     190,345 ns/iter (+/- 4,976)
test benchmarks::priority_change_on_small_double_queue     ... bench:          26 ns/iter (+/- 0)
test benchmarks::priority_change_on_small_double_queue_fx  ... bench:          20 ns/iter (+/- 0)
test benchmarks::priority_change_on_small_queue            ... bench:          15 ns/iter (+/- 0)
test benchmarks::priority_change_on_small_queue_fx         ... bench:          10 ns/iter (+/- 0)
test benchmarks::priority_change_on_small_queue_std        ... bench:       1,694 ns/iter (+/- 21)
test benchmarks::push_and_pop                              ... bench:          31 ns/iter (+/- 0)
test benchmarks::push_and_pop_double                       ... bench:          31 ns/iter (+/- 0)
test benchmarks::push_and_pop_double_fx                    ... bench:          24 ns/iter (+/- 1)
test benchmarks::push_and_pop_fx                           ... bench:          26 ns/iter (+/- 0)
test benchmarks::push_and_pop_min_on_large_double_queue    ... bench:         101 ns/iter (+/- 2)
test benchmarks::push_and_pop_min_on_large_double_queue_fx ... bench:          98 ns/iter (+/- 0)
test benchmarks::push_and_pop_on_large_double_queue        ... bench:         107 ns/iter (+/- 2)
test benchmarks::push_and_pop_on_large_double_queue_fx     ... bench:         106 ns/iter (+/- 2)
test benchmarks::push_and_pop_on_large_queue               ... bench:          84 ns/iter (+/- 1)
test benchmarks::push_and_pop_on_large_queue_fx            ... bench:          78 ns/iter (+/- 2)
test benchmarks::push_and_pop_on_large_queue_std           ... bench:          71 ns/iter (+/- 1)
test benchmarks::push_and_pop_std                          ... bench:           4 ns/iter (+/- 0)

使用标准队列更改优先级的方法如下

pq = pq.drain().map(|Entry(i, p)| {
    if i == 50_000 {
        Entry(i, p/2)
    } else {
        Entry(i, p)
    }
}).collect()

基准测试的解释是,此crate提供的数据结构通常比标准Binary Heap略慢。

在小型队列(小于10000个元素)中,使用上述代码从标准二叉堆中获得的change_priority函数,其性能远低于PriorityQueue和DoublePriorityQueue提供的函数。随着队列变大,PriorityQueue和DoublePriorityQueue的操作时间几乎相同,而标准队列的操作时间则会越来越长。

此外,随机弹出最小或最大元素的能力也带来了一定的代价,这在DoublePriorityQueue的所有操作中都有所体现,其速度低于PriorityQueue的相应操作。

贡献

请随时通过pull requests和/或issues来为此项目做出贡献。

所有贡献都必须符合与GNU LGPL版本3或任何后续版本以及MPL版本2.0兼容的许可协议。

变更

  • 0.7.0 实现了 push_increasepush_decrease 方便方法。
  • 0.6.0 允许使用自定义哈希器
  • 0.5.4 防止在扩展空队列时 panic
  • 0.5.3 重新实现 Default trait,避免了 P: Default 的要求
  • 0.5.2 修复文档格式
  • 0.5.1 为 iter_mut 添加了一些文档
  • 0.5.0 修复了 #7 实现 iter_mut 功能
  • 0.4.5 修复了 #6change_prioritychange_priority_by
  • 0.4.4 修复了 #6
  • 0.4.3 修复了 #4 改变 PriorityQueue 序列化的方式。请注意,旧的序列化 PriorityQueue 可能与新版本不兼容。API 应该保持不变。
  • 0.4.2 通过在实现中使用一些不安全的代码来提高性能。
  • 0.4.1 当使用 --features serde 编译时,支持 serde。将 serde 标记为可选功能,将 serde-test 标记为开发依赖。现在编译 crate 不会下载和编译 serde-test,也不会编译 serde(如果不需要的话)。
  • 0.4.0 当使用 cfg(serde) 编译时,支持 serde
  • 0.3.1 修复了 #3
  • 0.3.0 实现了 PartialEq 和 Eq 特性
  • 依赖项

    ~0.7–1MB
    ~18K SLoC