6 个版本
0.9.2 | 2024 年 8 月 3 日 |
---|---|
0.9.1 | 2024 年 4 月 6 日 |
0.9.0 | 2024 年 2 月 11 日 |
0.8.0 | 2024 年 1 月 28 日 |
0.7.1 | 2024 年 1 月 5 日 |
#157 在 数据结构 中
622 每月下载次数
155KB
2.5K SLoC
nodit
此软件包提供基于 BTreeMap
的离散区间树数据结构。
no_std
支持,并应与默认功能一起工作。
已实现多个离散区间树数据结构,以下是每个数据结构的简要概述及其使用场景:
结构 | 缩写 | 使用场景 |
---|---|---|
NoditMap |
非重叠离散区间树映射 | 一种通用的方式,用于将数据与不重叠的区间关联 |
NoditSet |
非重叠离散区间树集 | 当您想存储区间但不需要/不需要与每个区间关联数据时很有用 |
ZosditMap |
零重叠顺序离散区间树映射 | 对于时间图遍历算法和其他可能的事物很有用 |
Gqdit |
间隙查询离散区间树 | 当您有一组不同的非重叠区间并想在所有区间的集合上执行高效的间隙查询搜索时很有用 |
Copy
部分需要
由于与非 Copy
类型实现的复杂性,当前的数据结构要求区间类型和区间覆盖的点都是 Copy
。然而,使用 NoditMap
时使用的值类型不需要是 Copy
。实际上,用于值类型的唯一要求是有时是 Clone
或 Eq
use nodit::interval::ie;
use nodit::NoditMap;
let mut map = NoditMap::new();
map.insert_strict(ie(0, 5), true);
map.insert_strict(ie(5, 10), false);
assert_eq!(map.overlaps(ie(-2, 12)), true);
assert_eq!(map.contains_point(20), false);
assert_eq!(map.contains_point(5), true);
但仅限于某些方法,因此如果您不确定,请检查方法的特征限制。
use std::ops::{Bound, RangeBounds};
use nodit::interval::ie;
use nodit::{
DiscreteFinite, InclusiveInterval, Interval, NoditMap,
};
#[derive(Debug, Copy, Clone)]
enum Reservation {
// Start, End (Inclusive-Inclusive)
Finite(i8, i8),
// Start (Inclusive-Infinity)
Infinite(i8),
}
// First, we need to implement InclusiveInterval
impl InclusiveInterval<i8> for Reservation {
fn start(&self) -> i8 {
match self {
Reservation::Finite(start, _) => *start,
Reservation::Infinite(start) => *start,
}
}
fn end(&self) -> i8 {
match self {
Reservation::Finite(_, end) => *end,
Reservation::Infinite(_) => i8::MAX,
}
}
}
// Second, we need to implement From<Interval<i8>>
impl From<Interval<i8>> for Reservation {
fn from(value: Interval<i8>) -> Self {
if value.end() == i8::MAX {
Reservation::Infinite(value.start())
} else {
Reservation::Finite(
value.start(),
value.end().up().unwrap(),
)
}
}
}
// Next we can create a custom typed NoditMap
let reservation_map = NoditMap::from_slice_strict([
(Reservation::Finite(10, 20), "Ferris".to_string()),
(Reservation::Infinite(21), "Corro".to_string()),
])
.unwrap();
for (reservation, name) in reservation_map.overlapping(ie(16, 17))
{
println!(
"{name} has reserved {reservation:?} inside the interval 16..17"
);
}
for (reservation, name) in reservation_map.iter() {
println!("{name} has reserved {reservation:?}");
}
assert_eq!(
reservation_map.overlaps(Reservation::Infinite(0)),
true
);
示例使用包含-排除区间
示例使用自定义区间类型
此软件包旨在与离散类型(如Discrete
)一起使用,而不是与连续类型(如Continuous
)一起使用。例如,u8
是一种离散类型,但如果将其解析为十进制值,则String
是一种连续类型。
之所以如此,是因为常见的区间数学
运算取决于基础类型是离散类型还是连续类型。例如,5..=6
触及7..=8
,因为整数是离散的,但5.0..=6.0
不会触及7.0..=8.0
,因为存在值6.5
。
重要的是,这也使得包含/不包含端点的区间很容易处理,因为它们可以无损地在彼此之间转换。例如,3..6
等同于3..=5
。
有限性
目前,此软件包仅设计用于与有限类型(如u8
或i128
)一起使用,而不是与无限类型(如BigInt
)一起使用,后者来自num_bigint
软件包。这是因为如果类型是无限类型(如BigInt
),则get_key_value_at_point()
方法将无法从空映射中返回任何内容,因为它没有最大值。
当您不需要达到类型的顶部端点时,可以使用实际无限
来模拟无限类型,这是一种有用的技巧。例如,如果您使用u8
作为您的点类型,则可以创建一个包装类型,如下所示
use std::cmp::Ordering;
use nodit::DiscreteFinite;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum WithInfinity<T> {
Finite(T),
Infinity,
}
impl<T> Ord for WithInfinity<T>
where
T: Ord,
{
fn cmp(&self, other: &Self) -> Ordering {
match (self, other) {
(
WithInfinity::Finite(x),
WithInfinity::Finite(y),
) => x.cmp(y),
(WithInfinity::Finite(_), WithInfinity::Infinity) => {
Ordering::Less
}
(WithInfinity::Infinity, WithInfinity::Finite(_)) => {
Ordering::Greater
}
(WithInfinity::Infinity, WithInfinity::Infinity) => {
Ordering::Equal
}
}
}
}
impl<T> PartialOrd for WithInfinity<T>
where
T: Ord,
{
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<T> DiscreteFinite for WithInfinity<T>
where
T: DiscreteFinite,
{
const MIN: Self = WithInfinity::Finite(T::MIN);
const MAX: Self = WithInfinity::Infinity;
fn up(self) -> Option<Self>
where
Self: Sized,
{
match self {
WithInfinity::Finite(x) => match x.up() {
Some(y) => Some(WithInfinity::Finite(y)),
None => Some(WithInfinity::Infinity),
},
WithInfinity::Infinity => None,
}
}
fn down(self) -> Option<Self>
where
Self: Sized,
{
match self {
WithInfinity::Finite(x) => {
Some(WithInfinity::Finite(x.down()?))
}
WithInfinity::Infinity => {
Some(WithInfinity::Finite(T::MAX))
}
}
}
}
// And then you this means you can be explicit with when
// Infinity is encountered such as when it might be
// returned by `get_key_value_at_point()`, for example:
use nodit::interval::uu;
use nodit::{Interval, NoditMap};
let map: NoditMap<
WithInfinity<u8>,
Interval<WithInfinity<u8>>,
bool,
> = NoditMap::new();
let mut gap = map.get_key_value_at_point(WithInfinity::Finite(4));
assert_eq!(gap, Err(uu()));
无效区间
在此软件包中,并非所有区间都被认为是有效的区间。此软件包中使用的区间有效性的定义是:一个区间仅当它包含基础域的至少一个值时才有效。
例如,4..6
被视为有效,因为它包含值 4
和 5
,但是 4..4
被视为无效,因为它不包含任何值。另一个无效区间的例子是起始值大于结束值的区间。例如,5..2
或 100..=40
。
以下是几个区间及其有效性的示例
区间 | 是否有效 |
---|---|
0..=0 | 是 |
0..0 | 否 |
0..1 | 是 |
9..8 | 否 |
(Bound::Excluded(3), Bound::Excluded(4)) | 否 |
400..=400 | 是 |
重叠
如果有某个点同时包含在两个区间内,则称两个区间“重叠”。例如,2..4
和 2..6
重叠,但 2..4
和 4..8
不重叠。
接触
如果有两个区间没有重叠且它们之间没有其他值,则称这两个区间“接触”。例如,2..4
和 4..6
接触,但 2..4
和 6..8
不接触,同样 2..6
和 4..8
也不接触。
进一步阅读
请参阅维基百科上的数学区间文章:https://en.wikipedia.org/wiki/Interval_(mathematics)
特性
特性名称 | 描述 |
---|---|
默认 |
默认启用的隐式默认特性目前不激活任何其他特性 |
serde |
启用可选的 serde 依赖,并在本包中的所有类型上实现 serde::Serialize 和 serde::Deserialize |
致谢
我的许多灵感来自 rangemap
包。
底层的 BTreeMap 实现(《btree_monstrousity
》)是从 copse
包中启发和分叉而来的。
名称变更
此包之前名为 range_bounds_map
,大约于 2023-04-24 更名为 discrete_range_map
,因为它不再是一个准确的名称。
此包于 2023-01-02 再次重命名为 discrete_range_map
,原因是相同的,希望新名称的抽象性意味着它永远不会需要再次更改。
类似包
在搜索相关主题区域时,我找到了一些相关的crate,请注意阅读时的偏见。
- https://docs.rs/rangemap 与这个crate非常相似,但只能使用std
Range
和RangeInclusive
作为其map
和set
结构体(分别)的键。 - https://docs.rs/btree-range-map
- https://docs.rs/ranges 一个非常酷的库,用于完全通用的范围(与std::ops范围不同),还有一个用于存储它们的
Ranges
数据结构(不幸的是基于Vec)。 - https://docs.rs/intervaltree 允许重叠区间,但遗憾的是它是不可变的。
- https://docs.rs/nonoverlapping_interval_tree 与
rangemap
非常相似,但没有gaps()
函数,并且仅针对Range
而不是RangeInclusive
。而且也没有复杂的合并函数。 - https://docs.rs/unbounded-interval-tree 基于一篇2007年发表的论文实现的数据结构!它支持任何范围作为键,但遗憾的是,它使用一个非平衡的基于
Box<Node>
的树实现,但它也支持重叠范围,而我自己的库不支持。 - https://docs.rs/rangetree 我不确定这个库是做什么的,或者它不是什么,但它看起来是一个专门用于Range Tree的自定义红黑树/BTree实现。很有趣,但也相当古老(5年),并且使用了unsafe。
- https://docs.rs/rust-lapper 另一个类似不可变的(可以插入,但代价非常高)区间数据结构,针对大量相同大小的区间进行了优化,例如它们的主要用例基因组数据集。
- https://docs.rs/store-interval-tree 与这个crate和
rangemap
非常相似的区间树,具有许多相同的方法(以及大量的文档示例!)除了使用自定义的内部自平衡树实现。从我的文档阅读来看,不清楚它们是否支持重叠区间。一方面,他们的示例显示了重叠区间,但另一方面,他们的insert()
方法说“如果区间已经存在,则区间将被忽略”,所以也许它允许重叠但不允许重复的区间?在我看来,这是一个有点奇怪的选择。 - https://docs.rs/bio 和 https://docs.rs/rudac 都基本上与
store-interval-tree
相同,因为看起来store-interval-tree
是rudac
的区间树的分支。《code>bio 专门针对生物信息学。
依赖关系
~1MB
~24K SLoC