在计算机科学中,图(Graph)是一种非常重要的数据结构,广泛应用于社交网络、路径规划、编译器优化等领域。当图非常稀疏(即边的数量远小于顶点数的平方)时,使用传统的邻接矩阵会浪费大量内存。这时,十字链表(Orthogonal List)就成为一种高效的存储方式。
本文将带你一步步用 Rust 语言 实现一个基于十字链表的图结构。即使你是 Rust 新手或对图论不太熟悉,也能轻松跟上!
十字链表是一种用于表示有向图的数据结构,它结合了邻接表和逆邻接表的优点:
每条边在内存中只存储一次,但同时属于两个链表:起点的出边链表和终点的入边链表。这种“十字交叉”的结构因此得名。
Rust 的内存安全性和所有权系统非常适合构建复杂的数据结构。虽然指针操作在 Rust 中比 C/C++ 更严格,但通过 Rc<RefCell<T>> 等智能指针,我们可以在保证安全的同时实现灵活的图结构。
我们将定义三种结构体:
EdgeNode:表示一条边VertexNode:表示一个顶点OrthoGraph:整个图首先,在 Cargo.toml 中确保你有标准库支持(默认就有),无需额外依赖。
use std::collections::HashMap;use std::rc::Rc;use std::cell::RefCell;// 边节点:每条边只存一次#[derive(Debug)]struct EdgeNode { headvex: usize, // 弧头顶点位置 tailvex: usize, // 弧尾顶点位置 hlink: Option<Rc<RefCell<EdgeNode>>>, // 指向下一个入边 tlink: Option<Rc<RefCell<EdgeNode>>>, // 指向下一个出边}// 顶点节点#[derive(Debug)]struct VertexNode { data: String, firstin: Option<Rc<RefCell<EdgeNode>>>, // 指向第一条入边 firstout: Option<Rc<RefCell<EdgeNode>>>, // 指向第一条出边}// 十字链表图pub struct OrthoGraph { vertices: Vec<VertexNode>, vertex_map: HashMap<String, usize>, // 顶点名到索引的映射}impl OrthoGraph { pub fn new() -> Self { OrthoGraph { vertices: Vec::new(), vertex_map: HashMap::new(), } } // 添加顶点 pub fn add_vertex(&mut self, name: String) { if self.vertex_map.contains_key(&name) { return; // 避免重复 } let index = self.vertices.len(); self.vertex_map.insert(name.clone(), index); self.vertices.push(VertexNode { data: name, firstin: None, firstout: None, }); } // 添加有向边 from -> to pub fn add_edge(&mut self, from: &str, to: &str) { let from_idx = *self.vertex_map.get(from).expect("起始顶点不存在"); let to_idx = *self.vertex_map.get(to).expect("目标顶点不存在"); let mut new_edge = Rc::new(RefCell::new(EdgeNode { headvex: to_idx, tailvex: from_idx, hlink: None, tlink: None, })); // 插入到 from 的出边链表头部 { let mut from_vertex = &mut self.vertices[from_idx]; let old_firstout = from_vertex.firstout.take(); new_edge.borrow_mut().tlink = old_firstout; from_vertex.firstout = Some(new_edge.clone()); } // 插入到 to 的入边链表头部 { let mut to_vertex = &mut self.vertices[to_idx]; let old_firstin = to_vertex.firstin.take(); new_edge.borrow_mut().hlink = old_firstin; to_vertex.firstin = Some(new_edge); } } // 打印图结构(用于调试) pub fn print_graph(&self) { for (i, v) in self.vertices.iter().enumerate() { println!("顶点 {}: {}", i, v.data); println!(" 出边:"); let mut current = v.firstout.clone(); while let Some(edge) = current { let e = edge.borrow(); println!(" -> {} (顶点{})", self.vertices[e.headvex].data, e.headvex); current = e.tlink.clone(); } println!(" 入边:"); let mut current = v.firstin.clone(); while let Some(edge) = current { let e = edge.borrow(); println!(" <- {} (顶点{})", self.vertices[e.tailvex].data, e.tailvex); current = e.hlink.clone(); } println!(); } }} 下面是一个简单的使用案例:
fn main() { let mut graph = OrthoGraph::new(); // 添加顶点 graph.add_vertex("A".to_string()); graph.add_vertex("B".to_string()); graph.add_vertex("C".to_string()); // 添加有向边 graph.add_edge("A", "B"); graph.add_edge("A", "C"); graph.add_edge("B", "C"); // 打印图 graph.print_graph();} 运行后,你会看到每个顶点的入边和出边列表,清晰展示图的结构。
十字链表特别适合处理稀疏有向图,例如:
相比邻接矩阵,它节省空间;相比普通邻接表,它能快速获取入度信息——这正是 Rust 十字链表 的核心价值。
通过本文,你已经学会了如何用 Rust 实现一个功能完整的十字链表图结构。这不仅加深了你对 图数据结构 的理解,也展示了 Rust 在系统编程中的强大能力。无论是用于学术研究还是工业项目,这种 稀疏矩阵存储 方式都能带来显著的性能提升。
现在,你可以尝试扩展这个结构:添加权重、支持删除边、或实现拓扑排序算法!
本文由主机测评网于2025-12-03发表在主机测评网_免费VPS_免费云服务器_免费独立服务器,如有疑问,请联系我们。
本文链接:https://www.vpshk.cn/2025122256.html