|
| 1 | +""" |
| 2 | +First of all a tree of N nodes must have exactly N-1 edges. |
| 3 | +2 nodes need 1 edge to connect. 3 nodes need 2 edges to connect... |
| 4 | +Just draw one or two, you will know it. |
| 5 | +
|
| 6 | +Valid tree don't have cycles, there are two ways to detect it. |
| 7 | +DFS. and union find. Union find is more suitable in this sutuation. |
| 8 | +
|
| 9 | +1. Union find. |
| 10 | +We use an array 'markset' to store the root node of each node. [0] |
| 11 | +So if markset[1]==3, it means node1's root is node3. |
| 12 | +markset[6]==4, node6's root is node4. |
| 13 | +
|
| 14 | +we use find() to find the node's root. [1] |
| 15 | +For example if node1's root is node3. |
| 16 | +In the recursion, we find out that node3's root is node5. |
| 17 | +we return and set node5 as node1's real root. |
| 18 | +If a node don't have root then the root is itselves. |
| 19 | +
|
| 20 | +Imagine an edge. 1<->6 [2] |
| 21 | +union()'s mission is to find out if node1 and node6 share the same root before we know 1<->6 exist. |
| 22 | +If node1 and node6 share the same root before we know the edge 1<->6, |
| 23 | +There must be a cycle between node1, node6 and their root. |
| 24 | +
|
| 25 | +A special situation is that |
| 26 | +1<->2, 3<->4, 3<->5 (We have two trees that are not connected) |
| 27 | +1 and 3 will share -1 as 'root', this means that they are not connected. |
| 28 | +But a valid tree should be connected and only have one and only root. |
| 29 | +
|
| 30 | +The time complexity is O(NLogN), becuase we run a loop for every edges. |
| 31 | +And the number of edges is equal to N-1 |
| 32 | +for every edge we use find() to find the root of two nodes |
| 33 | +The recursion takes the height of the tree, which is LogN |
| 34 | +N is the number of nodes. |
| 35 | +
|
| 36 | +Space complexity is O(N). |
| 37 | +
|
| 38 | +2. DFS |
| 39 | +We use dfs to find if there are cycles in the tree. |
| 40 | +If we visit the node that we visited which means there is a cycle. |
| 41 | +Since this is an undirected map, we have to add both ways to the adjacent list. |
| 42 | +And everytime we use an edge, we need to remove the counter part of it to avoid repeat. |
| 43 | +finally, we have to check if we visit all the nodes. because there may be unconnected nodes. |
| 44 | +
|
| 45 | +The time complexity is O(N+E). Because this is DFS search in adjacent list. |
| 46 | +Space complexity is O(N). |
| 47 | +""" |
| 48 | +class Solution(object): |
| 49 | + def validTree(self, n, edges): |
| 50 | + def union(n1, n2): #[2] |
| 51 | + n1_root = find(n1) |
| 52 | + n2_root = find(n2) |
| 53 | + if n1_root==n2_root: return True |
| 54 | + markset[n2_root] = n1_root |
| 55 | + return False |
| 56 | + |
| 57 | + def find(node): #[1] |
| 58 | + if markset[node]==-1: return node |
| 59 | + return find(markset[node]) |
| 60 | + |
| 61 | + if len(edges)!=n-1: return False |
| 62 | + markset = [-1 for _ in xrange(n)] #[0] |
| 63 | + |
| 64 | + for edge in edges: |
| 65 | + if union(edge[0], edge[1]): return False |
| 66 | + |
| 67 | + return True |
| 68 | + |
| 69 | + |
| 70 | + def validTree(self, n, edges): |
| 71 | + if n!=len(edges)+1: return False |
| 72 | + |
| 73 | + graph = collections.defaultdict(list) |
| 74 | + stack = [] |
| 75 | + visited = set() |
| 76 | + |
| 77 | + for edge in edges: |
| 78 | + graph[edge[0]].append(edge[1]) |
| 79 | + graph[edge[1]].append(edge[0]) |
| 80 | + |
| 81 | + if len(edges)>0: |
| 82 | + stack.append(edges[0][0]) |
| 83 | + |
| 84 | + while stack: |
| 85 | + node = stack.pop() |
| 86 | + if node in visited: return False |
| 87 | + visited.add(node) |
| 88 | + for nb in graph[node]: |
| 89 | + stack.append(nb) |
| 90 | + graph[nb].remove(node) |
| 91 | + |
| 92 | + if len(visited)!=n: return False |
| 93 | + |
| 94 | + return True |
0 commit comments