use std::collections::HashMap;
// 定义图的节点
struct Node<T> {
data: T,
neighbors: Vec<usize>,
}
// 定义图结构
struct Graph<T> {
nodes: HashMap<usize, Node<T>>,
}
impl<T> Graph<T> {
// 添加节点
fn add_node(&mut self, data: T) -> usize {
let id = self.nodes.len();
self.nodes.insert(id, Node {
data,
neighbors: Vec::new(),
});
id
}
// 添加边
fn add_edge(&mut self, from: usize, to: usize) {
if let Some(node) = self.nodes.get_mut(&from) {
node.neighbors.push(to);
}
}
// 删除节点
fn remove_node(&mut self, id: usize) {
self.nodes.remove(&id);
// 移除所有指向该节点的边
for (_, node) in self.nodes.iter_mut() {
node.neighbors.retain(|&n| n != id);
}
}
// 深度优先遍历
fn dfs(&self, start: usize, visited: &mut Vec<bool>) {
visited[start] = true;
println!("Visiting node: {:?}", self.nodes[&start].data);
for &neighbor in self.nodes[&start].neighbors.iter() {
if!visited[neighbor] {
self.dfs(neighbor, visited);
}
}
}
// 广度优先遍历
fn bfs(&self, start: usize) {
let mut visited = vec![false; self.nodes.len()];
let mut queue = std::collections::VecDeque::new();
visited[start] = true;
queue.push_back(start);
while let Some(current) = queue.pop_front() {
println!("Visiting node: {:?}", self.nodes[¤t].data);
for &neighbor in self.nodes[¤t].neighbors.iter() {
if!visited[neighbor] {
visited[neighbor] = true;
queue.push_back(neighbor);
}
}
}
}
}
泛型生命周期灵活性拓展在设计中的运用及内存安全保障
- 泛型的使用:
- 在
Node
和Graph
结构体定义中使用泛型T
,这使得图节点可以存储任何类型的数据,增强了代码的通用性。例如,Node<T>
中的data: T
和Graph<T>
中的nodes: HashMap<usize, Node<T>>
,T
可以是i32
、String
或者自定义结构体等。
- 生命周期处理:
- 这里虽然没有显式地写出生命周期参数,但是Rust的自动生命周期推导机制在背后起作用。在
Graph
结构体方法中,例如add_node
、add_edge
、remove_node
等,参数和返回值的生命周期关系都能被正确推导。
- 对于
dfs
和bfs
遍历方法,self
参数表示对Graph
的不可变借用,其生命周期贯穿整个方法调用过程。visited
向量在dfs
中是可变借用,并且其生命周期也在方法调用期间有效。由于Rust的借用检查机制,确保了在同一时间内,对于同一数据不会有多个可变借用或者可变借用与不可变借用冲突的情况,从而保障了内存安全。
- 在处理复杂引用关系时,例如
Node
结构体中的neighbors
字段存储的是节点的索引而非直接引用,这避免了复杂的引用循环问题。同时,Rust的所有权和借用规则保证了在节点添加、删除和遍历过程中,内存的正确管理和安全访问。例如,remove_node
方法中,在移除节点时,同时更新其他节点的neighbors
字段,确保不存在悬空引用。