这是 LeetCode 3812 翻转树上最少边(Minimum Edge Toggles on a Tree)的 Rust 实现。

解题思路

核心观察:翻转一条边会同时改变其两个端点的颜色(0↔1)。每条边最多翻转一次。

自底向上 DFS:对于每个节点,若其子树返回需要翻转,则翻转该边,同时当前节点的翻转状态也被取反。最终根节点必须不需要翻转,否则无解。

Rust 实现

```rust
use std::collections::HashMap;

impl Solution {
    pub fn minimum_flips(n: i32, edges: Vec<Vec<i32>>, start: String, target: String) -> Vec<i32> {
        let n = n as usize;
        let start: Vec<char> = start.chars().collect();
        let target: Vec<char> = target.chars().collect();

        // 构建邻接表: 节点 -> [(邻居, 边索引)]
        let mut g: Vec<Vec<(usize, usize)>> = vec![vec![]; n];
        for (i, edge) in edges.iter().enumerate() {
            let a = edge[0] as usize;
            let b = edge[1] as usize;
            g[a].push((b, i));
            g[b].push((a, i));
        }

        let mut ans: Vec<usize> = Vec::new();

        // DFS 返回节点 a 当前是否需要被翻转
        fn dfs(
            a: usize,
            fa: i32,
            g: &Vec<Vec<(usize, usize)>>,
            start: &Vec<char>,
            target: &Vec<char>,
            ans: &mut Vec<usize>,
        ) -> bool {
            let mut rev = start[a] != target[a];

            for &(b, i) in &g[a] {
                if b as i32 == fa {
                    continue;
                }
                // 如果子树需要翻转边 (a,b)
                if dfs(b, a as i32, g, start, target, ans) {
                    ans.push(i);      // 翻转这条边
                    rev = !rev;       // 节点 a 的颜色也被翻转
                }
            }

            rev
        }

        // 如果根节点还需要翻转,说明无解
        if dfs(0, -1, &g, &start, &target, &mut ans) {
            return vec![-1];
        }

        // 升序排序并转换为 i32
        ans.sort();
        ans.into_iter().map(|x| x as i32).collect()
    }
}
```

复杂度

- 时间:O(n \log n),DFS 遍历 O(n),排序 O(n \log n)
- 空间:O(n),邻接表 + 递归栈

关键说明

要点    说明    
`rev = start[a] != target[a]`    节点 `a` 当前是否需要被翻转    
`dfs(b, a, ...)` 返回 `true`    子节点 `b` 需要被翻转,必须通过翻转边 `(a,b)` 解决    
`rev = !rev`    翻转边后,节点 `a` 的颜色也被改变    
根节点返回 `true` → `[-1]`    根没有父边,无法被翻转,说明无解    
每条边最多翻转一次    翻转两次等于没翻,所以方案唯一且最短

 

更多推荐