261. Graph Valid Tree

Given n nodes labeled from 0 to n-1 and a list of undirected edges (each edge is a pair of nodes), write a function to check whether these edges make up a valid tree.

Example 1:

Input: n = 5, and edges = [[0,1], [0,2], [0,3], [1,4]]
Output: true

Example 2:

Input: n = 5, and edges = [[0,1], [1,2], [2,3], [1,3], [1,4]]
Output: false

cinspiration:https://www.youtube.com/watch?v=vsIb9B84Rt8

思路:

valid tree需要满足:

  1. 除了跟节点,每个节点入度为1
  2. 只能有一个跟节点
  3. 不能有环 (有环也能出现每个节点入度为1的情况)

方法1: Union Find

思路:

建立一个祖先查询的vector,初始化为-1。当一个数的祖先为-1时说明它就是这一支/自己的祖先,否则要按照表中的数字继续向上查找。对每一对vertex,如果他们已经有相同的祖先,并且又被当前edge连起来,说明已经构成cycle,返回false。如果并不相连,在本循环中连起来,通过将x的祖先改为y:roots[x] = y。当循环过所有edge都没有被return false打断,说明没有出现cycle,此时需要再判断一步是否只存在一棵树。判断方法可以有两种:

  1. edges.size() == n- 1: 数的egde一定是|V| - 1条
  2. 是否只有一个祖先是-1 的节点
class Solution {
public:
    bool validTree(int n, vector<pair<int, int>>& edges) {
    	// 
        vector<int> roots(n, -1);
        
        for (auto edge: edges){
            int x = find(roots, edge.first);
            int y = find(roots, edge.second);
            if (x == y) {
                return false;
            }
            else {
                roots[x] = y;
            }
        }
        //int k = 0;
        //for (int i = 0; i < n; i++){
        //    if (roots[i]==-1) k++;
        //}
        //return k == 1;
		return edges.size() == n - 1;
    }
    
    int find(vector<int> & roots, int i){
        while (roots[i] != -1){
            i = roots[i];
        }
        return i;
    }
};

方法2: dfs

grandyang: http://www.cnblogs.com/grandyang/p/5257919.html

思路:
建立双向图,用dfs来检测是否有环。如果有则返回false,如果从一个节点开始并没有visit过所有的节点,返回false。

这里需要一个prev来记住谁是上一个节点,主要是因为这里是双向图,不记住会返回上一个节点并无限循环。

而之所以需要建双向图是因为事先不知道child/parent

class Solution {
public:
    bool validTree(int n, vector<pair<int, int>>& edges) {
       vector<vector<int>> graph(n, vector<int>(0));
       vector<int> visited(n, 0);
        for (auto edge: edges){
            graph[edge.first].push_back(edge.second);
            graph[edge.second].push_back(edge.first);
        }
        
        
        if (dfs(0, graph, visited, -1)) return false;
        
        for (auto i : visited){
            if (i == 0) return false;
        }
        
        return true;
    }
    
    bool dfs(int i, vector<vector<int>>& graph, vector<int> &visited, int prev){
        if (visited[i] == 1) return true;
        visited[i] = 1;
        
        for (int j: graph[i]){
            if (j != prev && dfs(j, graph, visited, i)){
                 return true;
                }        
        }    
        return false;
    }
};

方法3: bfs

思路:

建立双向图,用bfs来检测是否有环。如果已经访问过则返回false,否则将邻居节点全部入队,并将该节点的反向edge从邻居节点映射中删除,作用等同于dfs中的prev操作。以任意节点起始,最后检查是否所有节点都visited过。

// BFS
class Solution {
public:
    bool validTree(int n, vector<pair<int, int>>& edges) {
        vector<unordered_set<int>> g(n, unordered_set<int>());
        unordered_set<int> s{{0}};
        queue<int> q{{0}};
        for (auto a : edges) {
            g[a.first].insert(a.second);
            g[a.second].insert(a.first);
        }
        while (!q.empty()) {
            int t = q.front(); q.pop();
            for (auto a : g[t]) {
                if (s.count(a)) return false;
                s.insert(a);
                q.push(a);
                g[a].erase(t);
            }
        }
        return s.size() == n;
    }
};

方法4: topological sort

思路:

这道题也能用topological sort来解,主要就是,要忽视双向途中造成的环(用同样的prev trick),其他一切有环都返回fasle。最后判断是否还有没有visited的vertex。基本和dfs的方法是一样的。

class Solution {
public:
    bool validTree(int n, vector<vector<int>>& edges) {
        vector<vector<int>> graph(n, vector<int>(0));
        vector<int> visited(n, 0);
        for (auto e: edges) {
            graph[e[0]].push_back(e[1]);
            graph[e[1]].push_back(e[0]);
        }
        
        if (dfs(graph, visited, 0, -1)) return false;
        
        for (auto i: visited) {
            if (i == 0) return false;
        }
        return true;
    }
    
    bool dfs(vector<vector<int>> & graph, vector<int> & visited, int i, int prev) {
        if (visited[i] == 2) return false;
        if (visited[i] == 1) return true;
        
        visited[i] = 1;
        for (auto j: graph[i]) {
            if (j != prev && dfs(graph, visited, j, i)){
                return true;
            }
        }
        visited[i] = 2;
        return false;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值