多个可变借用图形数据结构

时间:2018-03-10 10:28:29

标签: rust directed-acyclic-graphs

我正在尝试编写一个程序,它将在有向图中找到图中最长的路径(即最大深度),该图总是一个有根或多根的树。

赋值的规范要求我使用DFS和memoization,但在执行DFS时会发生多个可变引用。还有其他办法吗?

我考虑使用HashMap而不是内部Graph字段,但它只会对HashMap的可变性产生相同的错误。我在Rust用户论坛和这里找到了其他几个问题,但是他们都没有给出如何解决这个问题的建议。我应该使用"不安全"代码或其他一些策略?

use std::io;

struct Node {
    neighbours: Vec<usize>,
    depth: usize,
    visited: bool,
}

impl Node {
    fn new() -> Node { Node { neighbours: Vec::new(), depth: 0, visited: false } }
    fn add_neighbour(&mut self, node: usize) { self.neighbours.push(node); }
    fn neighbourhood_size(&self) -> usize { self.neighbours.len() }
}

struct Graph {
    nodes: Vec<Node>,
    depth: usize,
}

impl Graph {
    fn new() -> Graph { Graph { nodes: Vec::new(), depth: 0} }
    fn nodes_number(&self) -> usize { self.nodes.len()}
    fn add_node(&mut self) { self.nodes.push(Node::new()); }
    fn node(&mut self, i: usize) -> &mut Node { &mut self.nodes[i] }

    fn dfs(graph: &mut Graph, index: usize) {
        if !graph.node(index).visited {
            graph.node(index).visited = true;
        }
        match graph.node(index).neighbourhood_size() == 0 {
            true => { graph.node(index).depth = 1; },
            false => {
                for &i in graph.node(index).neighbours.iter() {
                    // multiple mutable references 
                    Graph::dfs(graph, i);
                }
                graph.node(index).depth = 
                    1 + graph.node(index).
                    neighbours.iter().
                    map(|&x| graph.node(x).depth).
                    max().unwrap();
            }
        }
        if graph.node(index).depth > graph.depth {
            graph.depth = graph.node(index).depth;
        }
    }
}


fn main() {
    let mut input_line = String::new();
    io::stdin().read_line(&mut input_line);
    let n = input_line.trim().parse::<usize>().unwrap();
    // to avoid counting from 0 or excessive use of (-1)
    let mut graph = Graph::new(); graph.add_node();
    for _ in 0 .. n {
        let mut input_line = String::new();
        io::stdin().read_line(&mut input_line);
        let separated = input_line.
            split(" ").
            collect::<Vec<_>>();
        let u = separated[0].trim().parse::<usize>().unwrap();
        let v = separated[1].trim().parse::<usize>().unwrap();
        if graph.nodes_number() <= u { graph.add_node(); }
        if graph.nodes_number() <= v { graph.add_node(); }
        graph.node(u).add_neighbour(v);
    }
    let n = graph.nodes_number();
    for i in 1 .. n {
        if !graph.node(i).visited { Graph::dfs(&mut graph, i); }
    }
    println!("{}", graph.depth);
}

2 个答案:

答案 0 :(得分:1)

在迭代迭代之前,你可以迭代索引,而不是复制矢量:

for ni in 0..graph.node(index).neighbours.len() {
    let neighbour = graph.node(index).neighbours[ni];
    Graph::dfs(graph, neighbour);
}

neighbours向量仍然用于执行迭代,但不适用于整个迭代过程:

  1. graph.node(index).neighbours.len():一次在迭代开始时获取长度
  2. let neighbour = graph.node(index).neighbours[ni];:在每个迭代步骤中,使邻居获得当前索引
  3. 与复制方法一样,此解决方案基于约束,即您正在迭代的neighbours向量不会被调用dfs更改。

    您可以通过提供对图节点的不可变访问来解决代码中多个引用的其余问题:

    fn node_mut(&mut self, i: usize) -> &mut Node {
        &mut self.nodes[i]
    }
    fn node(&self, i: usize) -> &Node {
        &self.nodes[i]
    }
    

    仅在必要时通过node_mut使用可变访问权限。例如,添加邻居时:graph.node_mut(u).add_neighbour(v);

答案 1 :(得分:0)

您正在修改graph结构,同时迭代其中包含的向量。编译器无法在迭代期间验证您是否未在向量中添加或删除,这会使迭代器无效。这是错误的直观原因。

避免这种情况的最简单方法是在迭代之前获取向量的副本,这样编译器就可以看到迭代器没有改变。这有点不理想,但现在解决了错误。通过在进行比较之前将深度复制到变量中,可以以类似的方式解决另一个生命周期错误(但成本不高)。

use std::io;
use std::env;

struct Node {
    neighbours: Vec<usize>,
    depth: usize,
    visited: bool,
}

impl Node {
    fn new() -> Node {
        Node {
            neighbours: Vec::new(),
            depth: 0,
            visited: false,
        }
    }
    fn add_neighbour(&mut self, node: usize) {
        self.neighbours.push(node);
    }
    fn neighbourhood_size(&self) -> usize {
        self.neighbours.len()
    }
}

struct Graph {
    nodes: Vec<Node>,
    depth: usize,
}

impl Graph {
    fn new() -> Graph {
        Graph {
            nodes: Vec::new(),
            depth: 0,
        }
    }
    fn nodes_number(&self) -> usize {
        self.nodes.len()
    }
    fn add_node(&mut self) {
        self.nodes.push(Node::new());
    }
    fn node(&mut self, i: usize) -> &mut Node {
        &mut self.nodes[i]
    }

    fn dfs(graph: &mut Graph, index: usize) {
        if !graph.node(index).visited {
            graph.node(index).visited = true;
        }
        match graph.node(index).neighbourhood_size() == 0 {
            true => {
                graph.node(index).depth = 1;
            }
            false => {
                let neighbours = graph.node(index).neighbours.clone();
                for &i in neighbours.iter() {
                    // multiple mutable references
                    Graph::dfs(graph, i);
                }
                graph.node(index).depth = 1
                    + neighbours
                        .iter()
                        .map(|&x| graph.node(x).depth)
                        .max()
                        .unwrap();
            }
        }
        let depth = graph.node(index).depth;
        if depth > graph.depth {
            graph.depth = graph.node(index).depth;
        }
    }
}

fn main() {
    env::set_var("RUST_BACKTRACE", "1");
    let mut input_line = String::new();
    io::stdin().read_line(&mut input_line);
    let n = input_line.trim().parse::<usize>().unwrap();
    // to avoid counting from 0 or excessive use of (-1)
    let mut graph = Graph::new();
    graph.add_node();
    for _ in 0..n {
        let mut input_line = String::new();
        io::stdin().read_line(&mut input_line);
        let separated = input_line.split(" ").collect::<Vec<_>>();
        let u = separated[0].trim().parse::<usize>().unwrap();
        let v = separated[1].trim().parse::<usize>().unwrap();
        if graph.nodes_number() <= u {
            graph.add_node();
        }
        if graph.nodes_number() <= v {
            graph.add_node();
        }
        graph.node(u).add_neighbour(v);
    }
    let n = graph.nodes_number();
    for i in 1..n {
        if !graph.node(i).visited {
            Graph::dfs(&mut graph, i);
        }
    }
    println!("{}", graph.depth);
}

playground

如果您要修改您的方法,以便在搜索期间没有改变结构(即您将访问过的数据存储在其他地方),代码将在没有此副本的情况下工作。这对于并发使用也会更友好。