Skip to content

Commit 5b59747

Browse files
authored
Merge pull request #1062 from yolophg/main
[Helena] Week 12
2 parents 3c35866 + 1511706 commit 5b59747

File tree

5 files changed

+167
-0
lines changed

5 files changed

+167
-0
lines changed

non-overlapping-intervals/yolophg.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Time Complexity: O(N log N)
2+
# (1) sorting the intervals takes O(N log N), and
3+
# (2) iterating through them takes O(N).
4+
# (3) so the overall complexity is O(N log N).
5+
# Space Complexity: O(1) - only use a few extra variables (end and res), so the space usage is constant.
6+
7+
class Solution:
8+
def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int:
9+
# sort intervals by their ending time
10+
intervals.sort(key=lambda x: x[1])
11+
12+
# track the last non-overlapping interval's end
13+
end = float('-inf')
14+
# count the number of intervals we need to remove
15+
res = 0
16+
17+
for interval in intervals:
18+
# if it overlaps with the last interval
19+
if interval[0] < end:
20+
# need to remove this one
21+
res += 1
22+
else:
23+
# otherwise, update the end to the current interval's end
24+
end = interval[1]
25+
return res
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Time Complexity: O(N + E) - go through all nodes & edges.
2+
# Space Complexity: O(N) - store parent info for each node.
3+
4+
class Solution:
5+
def count_components(self, n: int, edges: List[List[int]]) -> int:
6+
7+
# set up the Union-Find structure
8+
parent = [i for i in range(n)] # initially, each node is its own parent
9+
rank = [1] * n # used for optimization in Union operation
10+
11+
# find function (with path compression)
12+
def find(node):
13+
if parent[node] != node:
14+
parent[node] = find(parent[node]) # path compression
15+
return parent[node]
16+
17+
# union function (using rank to keep tree flat)
18+
def union(node1, node2):
19+
root1, root2 = find(node1), find(node2)
20+
if root1 != root2:
21+
if rank[root1] > rank[root2]:
22+
parent[root2] = root1
23+
elif rank[root1] < rank[root2]:
24+
parent[root1] = root2
25+
else:
26+
parent[root2] = root1
27+
rank[root1] += 1
28+
return True # union was successful (i.e., we merged two components)
29+
return False # already in the same component
30+
31+
# connect nodes using Union-Find
32+
for a, b in edges:
33+
union(a, b)
34+
35+
# count unique roots (number of connected components)
36+
return len(set(find(i) for i in range(n)))
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Time Complexity: O(N) - go through the list twice (once to count, once to remove).
2+
# Space Complexity: O(1) - only use a few extra variables.
3+
4+
class Solution:
5+
def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
6+
# count the total number of nodes in the list
7+
N = 0
8+
curr = head
9+
while curr:
10+
N += 1
11+
curr = curr.next
12+
13+
# find the position of the node to remove (from the start)
14+
node_to_remove = N - n
15+
16+
# if need to remove the first node, just return the second node as the new head
17+
if node_to_remove == 0:
18+
return head.next
19+
20+
# traverse again to find the node right before the one we need to remove
21+
curr = head
22+
for i in range(N - 1):
23+
if (i + 1) == node_to_remove:
24+
# remove the nth node by skipping it
25+
curr.next = curr.next.next
26+
break
27+
curr = curr.next
28+
29+
return head

same-tree/yolophg.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Time Complexity: O(N) - visit each node once.
2+
# Space Complexity: O(N) in the worst case (skewed tree), O(log N) in the best case (balanced tree).
3+
4+
class Solution:
5+
def isSameTree(self, p: Optional[TreeNode], q: Optional[TreeNode]) -> bool:
6+
# if both trees are empty, they are obviously the same
7+
if p is None and q is None:
8+
return True
9+
10+
# if one tree is empty but the other is not, they can't be the same
11+
if p is None or q is None:
12+
return False
13+
14+
# if values of the current nodes are different, trees are not the same
15+
if p.val != q.val:
16+
return False
17+
18+
# recursively check both left and right subtrees
19+
return self.isSameTree(p.left, q.left) and self.isSameTree(p.right, q.right)
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Time Complexity:
2+
# (1) serialize(): O(N) because traverse all nodes once.
3+
# (2) deserialize(): O(N) because process each node once.
4+
5+
# Space Complexity:
6+
# (1) serialize(): O(N) to store the serialized string.
7+
# (2) deserialize(): O(N) for the queue and reconstructed tree.
8+
9+
class Codec:
10+
def serialize(self, root):
11+
# store the tree as a list of values (BFS style)
12+
ans = []
13+
if not root:
14+
return ""
15+
16+
queue = [root]
17+
while queue:
18+
cur = queue.pop()
19+
if not cur:
20+
ans.append("n") # use 'n' to represent null nodes
21+
continue
22+
ans.append(str(cur.val)) # add node value as string
23+
queue.append(cur.right) # right first (for consistency in deserialization)
24+
queue.append(cur.left) # then left
25+
26+
return ",".join(ans) # convert list to comma-separated string
27+
28+
def deserialize(self, data):
29+
if not data:
30+
return None
31+
32+
data = deque(data.split(",")) # convert string back to list
33+
root = TreeNode(int(data.popleft())) # first value is the root
34+
queue = [(root, 0)] # track parent nodes and child positions
35+
36+
while data:
37+
if not queue:
38+
return None # should never happen unless input is corrupted
39+
40+
cur = data.popleft()
41+
if cur == "n":
42+
val = None # null node, no need to create a TreeNode
43+
else:
44+
val = TreeNode(int(cur)) # create a new TreeNode
45+
46+
parent, cnt = queue.pop() # get the parent and which child we’re setting
47+
if cnt == 0:
48+
parent.left = val # assign left child
49+
else:
50+
parent.right = val # assign right child
51+
52+
cnt += 1 # move to the next child
53+
if cnt < 2:
54+
queue.append((parent, cnt)) # if parent still needs a right child, keep it in the queue
55+
if val:
56+
queue.append((val, 0)) # add new node to process its children later
57+
58+
return root

0 commit comments

Comments
 (0)