Skip to content

Commit 629f902

Browse files
authored
Merge pull request #1597 from seungriyou/main
[seungriyou] Week 12 Solutions
2 parents dc326db + 7bcbedb commit 629f902

File tree

5 files changed

+396
-0
lines changed

5 files changed

+396
-0
lines changed
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# https://leetcode.com/problems/non-overlapping-intervals/
2+
3+
from typing import List
4+
5+
class Solution:
6+
def eraseOverlapIntervals1(self, intervals: List[List[int]]) -> int:
7+
"""
8+
[Complexity]
9+
- TC: O(nlogn) (sort)
10+
- SC: O(1) (tim sort -> 최악의 경우 O(n)까지 가능)
11+
12+
[Approach]
13+
intervals를 start 기준으로 오름차순 정렬 후, greedy 하게 항상 작은 end를 가지는 interval을 선택한다.
14+
"""
15+
# sort asc by start
16+
intervals.sort(key=lambda x: x[0])
17+
18+
# initialize prev_e
19+
prev_e = intervals[0][0] - 1
20+
to_be_removed = 0
21+
22+
for s, e in intervals:
23+
# (1) overlapping : greedy하게 prev_e와 e 중 더 작은 값으로 prev_e를 업데이트하고, 제거할 interval 수 증가
24+
if prev_e > s:
25+
prev_e = min(prev_e, e)
26+
to_be_removed += 1
27+
# (2) non-overlapping : prev_e만 업데이트
28+
else:
29+
prev_e = e
30+
31+
return to_be_removed
32+
33+
def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int:
34+
"""
35+
[Complexity]
36+
- TC: O(nlogn) (sort)
37+
- SC: O(1) (tim sort -> 최악의 경우 O(n)까지 가능)
38+
39+
[Approach]
40+
이전 풀이의 (1) overlapping 단계에서 prev_e와 e 중 min을 고르는 로직을 제거하려면, intervals를 end 기준으로 오름차순 정렬하면 된다.
41+
"""
42+
# sort asc by end
43+
intervals.sort(key=lambda x: x[1])
44+
45+
# initialize prev_e
46+
prev_e = intervals[0][0] - 1
47+
to_be_removed = 0
48+
49+
for s, e in intervals:
50+
# (1) overlapping : greedy하게 prev_e와 e 중 더 작은 값으로 선택하면 되므로 prev_e를 그대로 두고, 제거할 interval 수 증가
51+
if prev_e > s:
52+
to_be_removed += 1
53+
# (2) non-overlapping : prev_e만 업데이트
54+
else:
55+
prev_e = e
56+
57+
return to_be_removed
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
# https://leetcode.com/problems/number-of-connected-components-in-an-undirected-graph/
2+
3+
from typing import List
4+
5+
class Solution:
6+
def countComponents_bfs(self, n: int, edges: List[List[int]]) -> int:
7+
"""
8+
[Complexity]
9+
- TC: O(v + e) (모든 edge & node 한 번씩 탐색)
10+
- SC: O(v + e) (graph)
11+
12+
[Approach]
13+
BFS로 visited를 기록해가며 connected component를 센다.
14+
"""
15+
from collections import deque
16+
17+
graph = [[] for _ in range(n)]
18+
for a, b in edges:
19+
graph[a].append(b)
20+
graph[b].append(a)
21+
22+
visited, res = set(), 0
23+
24+
def bfs(start):
25+
q = deque([start])
26+
visited.add(start)
27+
28+
while q:
29+
pos = q.popleft()
30+
for npos in graph[pos]:
31+
if npos not in visited:
32+
q.append(npos)
33+
visited.add(npos)
34+
35+
return
36+
37+
for i in range(n):
38+
if i not in visited:
39+
bfs(i)
40+
res += 1
41+
42+
return res
43+
44+
def countComponents_dfs(self, n: int, edges: List[List[int]]) -> int:
45+
"""
46+
[Complexity]
47+
- TC: O(v + e) (모든 edge & node 한 번씩 탐색)
48+
- SC: O(v + e) (graph)
49+
50+
[Approach]
51+
DFS로 visited를 기록해가며 connected component를 센다.
52+
"""
53+
graph = [[] for _ in range(n)]
54+
for a, b in edges:
55+
graph[a].append(b)
56+
graph[b].append(a)
57+
58+
visited, res = set(), 0
59+
60+
def dfs(pos):
61+
# 이전에 visited 포함 여부 확인하므로 base condition 생략 가능
62+
63+
visited.add(pos)
64+
65+
# recur
66+
for npos in graph[pos]:
67+
if npos not in visited:
68+
dfs(npos)
69+
70+
return
71+
72+
for i in range(n):
73+
if i not in visited:
74+
dfs(i)
75+
res += 1
76+
77+
return res
78+
79+
def countComponents(self, n: int, edges: List[List[int]]) -> int:
80+
"""
81+
[Complexity]
82+
- TC: O(v + e * α(v)) (모든 edge & node 한 번씩 탐색)
83+
- SC: O(v) (parent, set(...))
84+
85+
[Approach]
86+
edges를 iterate 하며 union-find 수행 후, parent의 종류의 개수를 세면 된다.
87+
parent의 종류의 개수를 셀 때는 다시 find_parent(x)로 찾아야 한다!
88+
"""
89+
90+
def find_parent(x):
91+
if x != parent[x]:
92+
parent[x] = find_parent(parent[x])
93+
return parent[x]
94+
95+
def union_parent(x, y):
96+
px, py = find_parent(x), find_parent(y)
97+
98+
if px < py:
99+
parent[py] = px
100+
else:
101+
parent[px] = py
102+
103+
parent = [i for i in range(n)]
104+
105+
for x, y in edges:
106+
union_parent(x, y)
107+
108+
return len(set(find_parent(i) for i in range(n)))
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
# https://leetcode.com/problems/remove-nth-node-from-end-of-list/
2+
3+
from typing import Optional
4+
5+
# Definition for singly-linked list.
6+
class ListNode:
7+
def __init__(self, val=0, next=None):
8+
self.val = val
9+
self.next = next
10+
11+
class Solution:
12+
def removeNthFromEnd_recur(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
13+
"""
14+
[Complexity]
15+
- TC: O(len)
16+
- SC: O(len) (call stack)
17+
18+
[Approach]
19+
재귀적으로 linked list를 확인하며 끝에서부터의 순서를 확인 후, 다음 node의 순서가 n과 같을 때 다음 node를 건너뛰도록 한다.
20+
head node를 제거해야 하는 경우(= 끝에서부터 n번째인 node가 head인 경우)에는, 재귀 함수 내에서 node 제거가 불가능하므로 head.next를 반환한다.
21+
"""
22+
23+
def check_order_from_end(curr):
24+
# base condition
25+
if not curr.next:
26+
return 1
27+
28+
# recur
29+
next_order_from_end = check_order_from_end(curr.next)
30+
# nth from end인 node를 건너뛰기
31+
if next_order_from_end == n:
32+
curr.next = curr.next.next
33+
34+
return next_order_from_end + 1
35+
36+
# head node를 제거해야 하는 경우라면, check_order_from_end() 내에서 node 제거가 불가능하므로 head.next를 반환해야 함
37+
if check_order_from_end(head) == n:
38+
return head.next
39+
40+
return head
41+
42+
def removeNthFromEnd_recur2(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
43+
"""
44+
[Complexity]
45+
- TC: O(len)
46+
- SC: O(len) (call stack)
47+
48+
[Approach]
49+
이전 풀이에서 head node를 제거해야 하는 경우를 따로 처리하지 않으려면, 주어진 head를 가리키는 dummy node(= prev)를 추가하고
50+
dummy node의 next node를 반환하면 된다.
51+
"""
52+
53+
def check_order_from_end(curr):
54+
# base condition
55+
if not curr.next:
56+
return 1
57+
58+
# recur
59+
next_order_from_end = check_order_from_end(curr.next)
60+
# nth from end인 node를 건너뛰기
61+
if next_order_from_end == n:
62+
curr.next = curr.next.next
63+
64+
return next_order_from_end + 1
65+
66+
prev = ListNode(next=head)
67+
check_order_from_end(prev)
68+
69+
return prev.next
70+
71+
def removeNthFromEnd_length(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
72+
"""
73+
[Complexity]
74+
- TC: O(n)
75+
- SC: O(1)
76+
77+
[Approach]
78+
linked list의 전체 길이를 구하고, head에서부터 (길이 - n - 1) 번 전진하여 node를 건너뛰면 된다.
79+
(2 pass)
80+
"""
81+
# linked list의 length 구하기
82+
length = 0
83+
curr = head
84+
while curr:
85+
length += 1
86+
curr = curr.next
87+
88+
# length == n라면, head를 제거
89+
if length == n:
90+
return head.next
91+
92+
# length - n - 1 번 이동
93+
curr = head
94+
for _ in range(length - n - 1):
95+
curr = curr.next
96+
97+
# node 제거
98+
curr.next = curr.next.next
99+
100+
return head
101+
102+
def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
103+
"""
104+
[Complexity]
105+
- TC: O(len)
106+
- SC: O(1)
107+
108+
[Approach]
109+
slow, fast의 two pointer를 이용해 반복문으로 풀 수 있다. (1 pass)
110+
1. fast를 n 번 전진
111+
2. fast가 끝에 도달한 경우, 첫 번째 node를 제거해야하므로 head.next 반환
112+
3. 현재 fast의 위치에서 slow와 fast를 함께 전진하면, fast가 끝에 도달할 때 slow는 뒤에서부터 n + 1번째 node임
113+
4. 뒤에서부터 n + 1번째인 node가 n - 1번째 node를 가리키도록 함
114+
"""
115+
slow = fast = head
116+
117+
# 1. fast를 n 번 전진
118+
for _ in range(n):
119+
fast = fast.next
120+
121+
# 2. fast가 끝에 도달한 경우, 첫 번째 node를 제거해야하므로 head.next 반환
122+
if not fast:
123+
return head.next
124+
125+
# 3. 현재 fast의 위치에서 slow와 fast를 함께 전진하면,
126+
# fast가 끝에 도달할 때 slow는 뒤에서부터 n + 1번째 node임
127+
while fast.next:
128+
slow, fast = slow.next, fast.next
129+
130+
# 4. 뒤에서부터 n + 1번째인 node가 n - 1번째 node를 가리키도록 함
131+
slow.next = slow.next.next
132+
133+
return head

same-tree/seungriyou.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# https://leetcode.com/problems/same-tree/
2+
3+
from typing import Optional
4+
5+
# Definition for a binary tree node.
6+
class TreeNode:
7+
def __init__(self, val=0, left=None, right=None):
8+
self.val = val
9+
self.left = left
10+
self.right = right
11+
12+
class Solution:
13+
def isSameTree(self, p: Optional[TreeNode], q: Optional[TreeNode]) -> bool:
14+
"""
15+
[Complexity]
16+
- TC: O(n)
17+
- SC: O(height) (call stack)
18+
19+
[Approach]
20+
재귀적으로 두 tree를 타고 내려가며 확인할 수 있다.
21+
각 단계에서 두 tree가 다르다고 판단할 수 있는 조건은
22+
(1) 한 쪽 node만 None이거나
23+
(2) 두 node의 값이 다른
24+
경우이다.
25+
"""
26+
# base condition
27+
if not p and not q:
28+
return True
29+
30+
# not same
31+
if not p or not q or p.val != q.val:
32+
return False
33+
34+
return self.isSameTree(p.left, q.left) and self.isSameTree(p.right, q.right)

0 commit comments

Comments
 (0)