4 个版本
0.2.0 | 2022年2月14日 |
---|---|
0.1.6 | 2022年2月12日 |
0.1.5 | 2022年2月10日 |
0.1.1 | 2022年2月10日 |
0.1.0 |
|
在 数据结构 中排名 1200
在 clocks 中使用
45KB
496 行
priq
使用原始二叉堆实现的优先队列(最小/最大堆)。
PriorityQueue
使用原始数组进行构建,以提高性能。
以下两点使这个 PriorityQueue
与其他现有的二叉堆实现不同
1 - 允许使用 PartialOrd
对数据进行排序。 - 其他所有最小-最大堆都需要对分数进行 完全排序(例如,应实现 Ord
特性)。这可能会成为一个问题,例如,当你想要根据浮点分数对项目进行排序时,这些分数没有实现 Ord
特性。 - 由于部分排序,不可比较的值被放在队列的末尾。只有在所有可比较的元素都被 pop
后,才会看到不可比较的值。 - 你可以在这里了解更多关于 Rust 实现 Ord
、PartialOrd
以及它们之间区别的信息 这里
2 - 将分数和要存储的项目分开。 - 这使得关联项目实现任何排序变得更容易。 - 使评估项目的顺序变得更加容易。
3 - 具有相同分数的项目存储在第一个可用的空闲空间中。 - 这为大量条目提供了性能提升。
4 - 易于使用!
你可以在我的博客 上 阅读更多关于这个软件包的信息
实现
具有为 score
和相关 item
定义的参数的最小-最大堆!
Default
实现是一个最小堆,其中顶节点(根)是得分最低的元素
10
/ \
58 70
/ \ / \
80 92 97 99
父节点的值小于其子节点。
每个父节点,包括顶节点(根节点),都小于或等于其右子节点。
PriorityQueue
允许重复的分数/项目值。当你使用具有相似分数的项目放入队列时,新条目将存储在内存中的第一个空位置。这提供了增量性能提升(而不是通过使用关联项目作为优先级评估的次要工具来解决)。此外,这种实现形式不强制要求元素 T
实现任何排序。这保证了顶节点始终是值最小的。
您可以初始化一个空的 PriorityQueue
,然后添加项目
use priq::PriorityQueue;
// create queue with `usize` key and `String` elements
let pq: PriorityQueue<usize, String> = PriorityQueue::new();
或者您可以对 Vec
和/或 slice
进行 堆化
use priq::PriorityQueue;
let pq_from_vec = PriorityQueue::from(vec![(5, 55), (1, 11), (4, 44)]);
let pq_from_slice = PriorityQueue::from([(5, 55), (1, 11), (4, 44)]);
部分排序
因为 priq
允许只实现 PartialOrd
的 score
参数,所以无法比较的元素将被评估并放在队列的后面
use priq::PriorityQueue;
let mut pq: PriorityQueue<f32, isize> = PriorityQueue::new();
pq.put(1.1, 10);
pq.put(f32::NAN, -1);
pq.put(2.2, 20);
pq.put(3.3, 30);
pq.put(f32::NAN, -3);
pq.put(4.4, 40);
(1..=4).for_each(|i| assert_eq!(i * 10, pq.pop().unwrap().1));
// NAN scores will not have deterministic order
// they are just stored after all the comparable scores
assert!(0 > pq.pop().unwrap().1);
assert!(0 > pq.pop().unwrap().1);
时间复杂度
此数据结构的标准用法是将元素 put
到队列中,并 pop
以删除队列顶部的元素,并检查队列顶部的元素。存储的元素结构是通过使用具有连续内存位置的数组实现的平衡树。这允许维护放入元素之间的正确父子关系。
大O符号的时间复杂度
方法 | 时间复杂度 |
---|---|
put |
O(log(n)) |
pop |
O(log(n)) |
peek |
O(1) |
您也可以使用循环遍历元素,但返回的切片将不会正确排序,因为每次插入和删除后堆都会重新平衡。如果您想以正确的优先级获取项目,请在循环中调用 pop
直到它返回 None
。
自定义 struct
如果您想要自定义 struct
而不是有一个独立的和特定的分数,您可以传递 struct
的克隆作为 score
和相关值,但在这种情况下,我建议使用 BinaryHeap
,因为它更适合目的。
最小堆
如果您想要最大堆而不是最小堆,其中最高分的元素在顶部,您可以使用 Reverse
或自定义 [Ord
] 实现来具有自定义的优先级逻辑。
示例
use priq::PriorityQueue;
use std::cmp::Reverse;
let mut pq: PriorityQueue<Reverse<u8>, String> = PriorityQueue::new();
pq.put(Reverse(26), "Z".to_string());
pq.put(Reverse(1), "A".to_string());
assert_eq!(pq.pop().unwrap().1, "Z");
合并和组合
您可以将另一个优先级队列合并到这个队列中。右侧的优先级队列将排空到左侧的优先级队列中。
示例
use priq::PriorityQueue;
let mut pq1 = PriorityQueue::from([(5, 55), (6, 66), (3, 33), (2, 22)]);
let mut pq2 = PriorityQueue::from([(4, 44), (1, 11)]);
pq1.merge(&mut pq2);
// at this point `pq2` is empty
assert_eq!(6, pq1.len());
assert_eq!(11, pq1.peek().unwrap().1);
您还可以使用 +
运算符合并两个优先级队列。操作数将保持不变。新优先级队列将通过克隆和合并它们来构建。
示例
use priq::PriorityQueue;
let pq1 = PriorityQueue::from([(5, 55), (1, 11), (4, 44), (2, 22)]);
let pq2 = PriorityQueue::from([(8, 44), (1, 22)]);
let res = pq1 + pq2;
assert_eq!(6, res.len());
assert_eq!(11, res.peek().unwrap().1);
性能
这是 priq::PriorityQueue
的基准测试结果
priq 基准测试 |
中位数 | 纳秒 | 标准差 |
---|---|---|---|
pq_pop_100 | 146 | ns/iter | (+/- 1) |
pq_pop_100k | 291,818 | ns/iter | (+/- 5,686) |
pq_pop_10k | 14,129 | ns/iter | (+/- 39) |
pq_pop_1k | 1,646 | ns/iter | (+/- 32) |
pq_pop_1mil | 16,517,047 | ns/iter | (+/- 569,128 |
pq_put_100 | 488 | ns/iter | (+/- 21) |
pq_put_100k | 758,422 | ns/iter | (+/- 13,961) |
pq_put_100k_wcap | 748,824 | ns/iter | (+/- 7,926) |
pq_put_10k | 80,668 | ns/iter | (+/- 1,324) |
pq_put_1k | 8,769 | ns/iter | (+/- 78) |
pq_put_1mil | 6,728,203 | ns/iter | (+/- 76,416) |
pq_put_1mil_wcap | 6,622,341 | ns/iter | (+/- 77,162) |
与 std::collections::BinaryHeap
的比较
BinaryHeap 基准测试 |
中位数 | 纳秒 | 标准差 |
---|---|---|---|
bh_pop_100 | 272 | ns/iter | (+/- 90) |
bh_pop_100k | 171,071 | ns/iter | (+/- 6,131) |
bh_pop_10k | 13,904 | ns/iter | (+/- 130) |
bh_pop_1k | 1,847 | ns/iter | (+/- 6) |
bh_pop_1mil | 8,772,066 | ns/iter | (+/- 611,613) |
bh_push_100 | 857 | ns/iter | (+/- 50) |
bh_push_100k | 943,465 | ns/iter | (+/- 108,698) |
bh_push_10k | 92,807 | ns/iter | (+/- 7,930) |
bh_push_1k | 8,606 | ns/iter | (+/- 639) |
bh_push_1mil | 12,946,815 | ns/iter | (+/- 900,347) |
项目是在 MIT 许可下分发的。有关更多信息,请参阅 LICENSE
。
依赖项
~310KB