Crates.io | rust-algo |
lib.rs | rust-algo |
version | 0.2.1 |
source | src |
created_at | 2023-10-17 08:03:36.454508 |
updated_at | 2023-11-06 06:36:41.161289 |
description | rust algorithms |
homepage | |
repository | |
max_upload_size | |
id | 1005614 |
size | 46,344 |
Rust 实现链表等数据结构
链表
双向链表
图
基础架构
Rc
或 unsafe
,而是使用 Arena 技术,类似 对象池ArenaList 的管理方法
ArenaList.nodes: Vec<Option<T>>
用来存放数据对象
nexts: Vec<Option<usize>>
用来存放下一个节点的索引。若为 None,表示没有下一个节点。
维护一个 holes: Vec<usize>
用来存放运行过程中产生的孔洞。代码里的 holes
用 stack 的方式还使用,也可以用 queue 的方式来使用(需要换成别的数据结构以保证性能)
统一使用带 dummy 的节点,可以降低代码复杂性。
LinkedList.root: unsize
代表链表 dummy 节点位置。已有节点上生成另一个链表,只需要 new 一个新的 LinkedList。
ArenaList 数据类型的取舍
可以是 nodes: Vec<Option<T>>
+ nexts: Vec<Option<usize>>
也可以是 nodes: Vec<NodeInfo<T>>
,其中 NodeInfo{data, next_idx}
应该没有优劣,我实现的是前者
ArenaList 要唯一
如果每个链表对应自己的 ArenaList,那么链表的 "拆分"、"合并" 这类原本 O(1) 的操作,会有大量 nodes 在不同 Vec 上批量移动。复杂度变成 O(n)
所以多个链表共用同一个 ArenaList. Tree/Graph 这类允许合并/拆分的数据结构也是一样。
维护 holes 的缺点和优点
compact
因此,对 Graph
需要增加一个 compact
方法来清理孔洞。
nodes.pop()
放入 nodes[holes.pop()]
。也就是最后一个有效节点放入第一个孔洞中删除只需要置 None, 判断为空的语句也相应调整。
插入新节点时,不再寻找孔洞,而是插入到末尾。
使用 holes
维护空洞的优缺点
compact()
时,遇到尾部是节点的情况,需要删除 hole
的第一个元素,但 holes: Vec<usize>
对这样的操作复杂度 为 O(n)
prev: Vec<usize>
来记录节点的上游。
prev
)。compact()
处理单个空洞效率是 O(1),总的效率为 O(m)。如果不维护 holes
总的效率为 O(n)又改进
next
和 prev
,并且及时处理 节点为 None 的情况。那么就压根不会出现空洞。holes
,也不需要 compact 方法了。node: Vec<Option<Node<T>>>
可以改为 node: Vec<Node<T>>
swap_remove()
达到 O(1) 性能,而不是 remove()
的 O(n) 性能