[LeetCode] 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

Note: you can assume that no duplicate edges will appear in edges. Since all edges are undirected, [0,1] is the same as [1,0] and thus will not appear together in edges.

 

這道題給了一個無向圖,讓我們來判斷其是否為一棵樹,如果是樹的話,所有的節點必須是連接的,也就是說必須是連通圖,而且不能有環,所以焦點就變成了驗證是否是連通圖和是否含有環。首先用 DFS 來做,根據 pair 來建立一個圖的結構,用鄰接鏈表來表示,還需要一個一位數組v來記錄某個結點是否被訪問過,然后用 DFS 來搜索結點0,遍歷的思想是,當 DFS 到某個結點,先看當前結點是否被訪問過,如果已經被訪問過,說明環存在,直接返回 false,如果未被訪問過,現在將其狀態標記為已訪問過,然后到鄰接鏈表里去找跟其相鄰的結點繼續遞歸遍歷,注意還需要一個變量 pre 來記錄上一個結點,以免回到上一個結點,這樣遍歷結束后,就把和結點0相鄰的節點都標記為 true,然后再看v里面是否還有沒被訪問過的結點,如果有,則說明圖不是完全連通的,返回 false,反之返回 true,參見代碼如下:

 

解法一:

// DFS
class Solution {
public:
    bool validTree(int n, vector<pair<int, int>>& edges) {
        vector<vector<int>> g(n, vector<int>());
        vector<bool> v(n, false);
        for (auto a : edges) {
            g[a.first].push_back(a.second);
            g[a.second].push_back(a.first);
        }
        if (!dfs(g, v, 0, -1)) return false;
        for (auto a : v) {
            if (!a) return false;
        }
        return true;
    }
    bool dfs(vector<vector<int>> &g, vector<bool> &v, int cur, int pre) {
        if (v[cur]) return false;
        v[cur] = true;
        for (auto a : g[cur]) {
            if (a != pre) {
                if (!dfs(g, v, a, cur)) return false;
            }
        }
        return true;
    }
};

 

下面來看 BFS 的解法,思路很相近,需要用 queue 來輔助遍歷,這里沒有用一維向量來標記節點是否訪問過,而是用了一個 HashSet,如果遍歷到一個節點,在 HashSet 中沒有,則加入 HashSet,如果已經存在,則返回false,還有就是在遍歷鄰接鏈表的時候,遍歷完成后需要將結點刪掉,參見代碼如下:

 

解法二:

// 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;
    }
};

 

我們再來看 Union Find 的方法,這種方法對於解決連通圖的問題很有效,思想是遍歷節點,如果兩個節點相連,將其 roots 值連上,這樣可以找到環,初始化 roots 數組為 -1,然后對於一個 pair 的兩個節點分別調用 find 函數,得到的值如果相同的話,則說明環存在,返回 false,不同的話,將其 roots 值 union 上,參見代碼如下:

 

解法三:

// Union Find
class Solution {
public:
    bool validTree(int n, vector<pair<int, int>>& edges) {
        vector<int> roots(n, -1);
        for (auto a : edges) {
            int x = find(roots, a.first), y = find(roots, a.second);
            if (x == y) return false;
            roots[x] = y;
        }
        return edges.size() == n - 1;
    }
    int find(vector<int> &roots, int i) {
        while (roots[i] != -1) i = roots[i];
        return i;
    }
};

 

Github 同步地址:

https://github.com/grandyang/leetcode/issues/261

 

類似題目:

Number of Islands II

Number of Connected Components in an Undirected Graph

 

參考資料:

https://leetcode.com/problems/graph-valid-tree/

https://leetcode.com/problems/graph-valid-tree/discuss/69018/AC-Java-Union-Find-solution

https://leetcode.com/problems/graph-valid-tree/discuss/69042/AC-Java-Graph-DFS-solution-with-adjacency-list

https://leetcode.com/problems/graph-valid-tree/discuss/69019/Simple-and-clean-c%2B%2B-solution-with-detailed-explanation.

 

LeetCode All in One 題目講解匯總(持續更新中...)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM