# 261. Graph Valid Tree

**<font color=red>难度: Medium</font>**

## 刷题内容

> 原题连接

* https://leetcode.com/problems/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.
```

## 解题方案

> 思路 1
******- 时间复杂度: O(V * E)******- 空间复杂度: O(V)******

graph 为 tree 两个条件：

- 这个图完全connected
- 没有cycle

偷懒AC代码，直接在323题，Number of Connected Components in an Undirected Graph上改的AC代码：

beats 82.10%

```python
class Graph:
    def __init__(self, n, edges):
        self.edges = edges
        self.n = n
        self.uf = [i for i in range(n)]

    def find(self, x, uf):
        while x != uf[x]:
            uf[x] = uf[uf[x]]
            x = uf[x]
        return uf[x]

    def union(self, x, y, uf):
        x_root = self.find(x, uf)
        y_root = self.find(y, uf)
        uf[x_root] = y_root

    def hasCycle(self):
        for node1, node2 in self.edges:
            if self.find(node1, self.uf) == self.find(node2, self.uf):  # cycle exists
                return True
            else:
                self.union(node1, node2, self.uf)
        return False

    def fullyConnected(self):
        self.hasCycle()  # 必须先调用一下这段代码实现preprocess
        return len(set(self.find(i, self.uf) for i in self.uf)) == 1  # fully connected
    
class Solution:
    def validTree(self, n, edges):
        """
        :type n: int
        :type edges: List[List[int]]
        :rtype: bool
        """
        graph = Graph(n, edges)
        return not graph.hasCycle() and graph.fullyConnected()
```

> 思路 2
******- 时间复杂度: O(V + E)******- 空间复杂度: O(V)******

- DFS判断是否从起始点可以走完所有点（是否完全联通）
- 中间碰到一个点，除了它来自的那个点(prev_v)以外，它其余的所有neghbor都应该是没有被访问过的（否则有环）

beats 36.01%

```python
class Graph(object):
    def __init__(self, vertex_cnt, edges):
        self.vertex_cnt = vertex_cnt
        self.graph = collections.defaultdict(list)
        self.visited = [False] * self.vertex_cnt
        for node1, node2 in edges:
            self.addEdge(node1, node2)

    def addEdge(self, v1, v2):
        self.graph[v1].append(v2)
        self.graph[v2].append(v1)

    def hasCycleHelper(self, v, visited, prev_v):
        self.visited[v] = True
        for i in self.graph[v]:
            if not visited[i]:
                if self.hasCycleHelper(i, visited, v):
                    return True
            else:
                if i != prev_v:
                    return True
        return False

    def hasCycle(self):
        if self.hasCycleHelper(0, self.visited, -1):
            return True
        return False

    def fullyConnected(self):
        return all(self.visited)
    
class Solution:
    def validTree(self, n, edges):
        """
        :type n: int
        :type edges: List[List[int]]
        :rtype: bool
        """
        graph = Graph(n, edges)
        return not graph.hasCycle() and graph.fullyConnected()
```





