Skip to content

Commit 3887c3b

Browse files
authored
Merge pull request #1034 from dusunax/main
[SunaDu] Week 11
2 parents b5d86bb + ef969af commit 3887c3b

File tree

3 files changed

+177
-0
lines changed

3 files changed

+177
-0
lines changed

graph-valid-tree/dusunax.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
'''
2+
# 261. Graph Valid Tree
3+
4+
## What constitutes a 🌲
5+
1. it's a graph.
6+
2. Connected: edges == n - 1, visited node count == n
7+
3. Acyclic: there is no cycle.
8+
9+
## Approach A. DFS
10+
use DFS to check if there is a cycle in the graph.
11+
- if there were no cycle & visited node count == n, return True.
12+
13+
## Approach B. Disjoint Set Union (서로소 집합)
14+
use Disjoint Set Union to check if there is a cycle in the graph.
15+
- if you find a cycle, return False immediately.
16+
- if you find no cycle, return True.
17+
18+
### Union Find Operation
19+
- Find: find the root of a node.
20+
- if the root of two nodes is already the same, there is a cycle.
21+
- Union: connect two nodes.
22+
23+
## Approach Comparison
24+
- **A. DFS**: simple and easy to understand.
25+
- **B. Disjoint Set Union**: quicker to check if there is a cycle. if there were more edges, Union Find would be faster.
26+
'''
27+
class Solution:
28+
def validTreeDFS(self, n: int, edges: List[List[int]]) -> bool:
29+
if len(edges) != n - 1:
30+
return False
31+
32+
graph = [[] for _ in range(n)]
33+
for node, neighbor in edges:
34+
graph[node].append(neighbor)
35+
graph[neighbor].append(node)
36+
37+
visited = set()
38+
def dfs(node):
39+
visited.add(node)
40+
for neighbor in graph[node]:
41+
if neighbor not in visited:
42+
dfs(neighbor)
43+
44+
dfs(0)
45+
return len(visited) == n
46+
47+
def validTreeUnionFind(self, n: int, edges: List[List[int]]) -> bool:
48+
if len(edges) != n - 1:
49+
return False
50+
51+
parent = [i for i in range(n)]
52+
53+
def find(x):
54+
if x == parent[x]:
55+
return x
56+
parent[x] = find(parent[x])
57+
return parent[x]
58+
59+
def union(x, y):
60+
parent[find(x)] = find(y)
61+
62+
for node, neighbor in edges:
63+
if find(node) == find(neighbor):
64+
return False
65+
union(node, neighbor)
66+
67+
return True
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
'''
2+
# 104. Maximum Depth of Binary Tree
3+
4+
use DFS to find the maximum depth of the binary tree.
5+
6+
## A. if we use a helper function (not a good idea)
7+
```
8+
helper function explanation:
9+
- store the max_depth in the nonlocal variable.(outside of the helper function)
10+
- base case: if the node is None, update the max_depth and return.
11+
- in the helper function, do recursive call for the left and right children of the node.
12+
- update the count for the depth of the tree.
13+
- update the max_depth when the node is a leaf node's children.
14+
```
15+
16+
## B. return the max_depth directly
17+
👉 why helper function is not necessary?
18+
recursion function can return the max_depth directly.
19+
remove side effect & non-local variable.
20+
21+
## TC is O(n)
22+
23+
visit each node once for checking if it is a leaf node's children.
24+
25+
## SC is O(h)
26+
27+
h for height of the tree
28+
'''
29+
class Solution:
30+
'''
31+
A. first approach with side effect
32+
'''
33+
def maxDepthWithHelper(self, root: Optional[TreeNode]) -> int:
34+
max_depth = 0
35+
36+
def helper(node, count):
37+
nonlocal max_depth
38+
if node is None:
39+
max_depth = max(max_depth, count)
40+
return
41+
42+
helper(node.left, count+1)
43+
helper(node.right, count + 1)
44+
45+
helper(root, max_depth)
46+
47+
return max_depth
48+
49+
'''
50+
B. return the max_depth directly.
51+
- more concise & readable
52+
- no side effect & non-local variable
53+
'''
54+
def maxDepth(self, root: Optional[TreeNode]) -> int:
55+
if root is None:
56+
return 0
57+
58+
left_count = self.maxDepth(root.left)
59+
right_count = self.maxDepth(root.right)
60+
61+
return max(left_count, right_count) + 1

reorder-list/dusunax.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
'''
2+
# 143. Reorder list
3+
use two pointers for each steps.
4+
5+
1. finding the middle
6+
- two pointers: slow, fast
7+
- move slow 1, fast 2 until fast reaches the end.
8+
2. reversing the second half
9+
- two pointers: prev, curr
10+
- start from slow to end, do common reverse linked list operation.
11+
- need to break the links to halves beforehand.
12+
3. merging first & second
13+
- two pointers: frist, second
14+
- merge second between first, until second is None
15+
16+
## TC is O(n)
17+
- find the middle: O(n)
18+
- reverse the second half: O(n)
19+
- merge the two halves: O(n)
20+
21+
## SC is O(1)
22+
- no extra space is used.
23+
'''
24+
class Solution:
25+
def reorderList(self, head: Optional[ListNode]) -> None:
26+
# 1. finding the middle
27+
slow, fast = head, head
28+
while fast and fast.next:
29+
slow = slow.next
30+
fast = fast.next.next
31+
32+
# 2. reversing second half
33+
second_not_reversed = slow.next
34+
slow.next = None
35+
prev, curr = None, second_not_reversed
36+
while curr:
37+
temp = curr.next
38+
curr.next = prev
39+
prev = curr
40+
curr = temp
41+
42+
# 3. merging first & second
43+
first, second = head, prev
44+
while second:
45+
temp1, temp2 = first.next, second.next
46+
first.next = second
47+
second.next = temp1
48+
first = temp1
49+
second = temp2

0 commit comments

Comments
 (0)