Skip to content

Commit b185c95

Browse files
authored
Merge pull request #1607 from seungriyou/main
[seungriyou] Week 13 Solutions
2 parents a68c8a4 + 80c82a8 commit b185c95

File tree

5 files changed

+251
-0
lines changed

5 files changed

+251
-0
lines changed
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# https://leetcode.com/problems/find-median-from-data-stream/
2+
3+
import heapq
4+
5+
class MedianFinder:
6+
"""
7+
[Time Complexity]
8+
- addNum(): O(logn) (heappush / heappop -> 힙 속성 유지 위해 트리 높이만큼 swap => sift-up / sift-down)
9+
- findMedian(): O(1)
10+
11+
[Approach]
12+
findMedian을 O(1)에 수행하기 위해서는 addNum을 할 때마다 데이터를 작은 부분 / 큰 부분으로 절반씩 나누어서 유지해야 한다.
13+
이때, 가능한 케이스별 median을 구하는 방법은 다음과 같다.
14+
- 두 절반의 길이가 같다면 median = ((작은 부분의 가장 큰 값) + (큰 부분의 가장 작은 값)) / 2
15+
- 두 절반의 길이가 다르다면 (큰 부분의 길이가 작은 부분의 길이보다 1 큰 경우라고 가정) median = (큰 부분의 가장 작은 값)
16+
따라서 작은 부분 / 큰 부분을 각각 max heap / min heap으로 관리하며, addNum 할 때마다 다음은 동작을 수행한다.
17+
- 두 절반의 길이가 같다면, 작은 부분에 push -> pop 한 결과를 큰 부분에 push
18+
- 두 절반의 길이가 다르다면, 큰 부분에 push -> pop 한 결과를 작은 부분에 push
19+
(파이썬에서 max heap을 사용하기 위해서는 min heap에 부호 반전인 수를 넣음으로써 구현하는 것에 유의한다!)
20+
"""
21+
22+
def __init__(self):
23+
self.lo = [] # 절반 중 작은 부분
24+
self.hi = [] # 절반 중 큰 부분
25+
26+
def addNum(self, num: int) -> None:
27+
if len(self.lo) == len(self.hi):
28+
heapq.heappush(self.hi, -heapq.heappushpop(self.lo, -num)) # heappushpop: heap을 한 번만
29+
else:
30+
heapq.heappush(self.lo, -heapq.heappushpop(self.hi, num))
31+
32+
def findMedian(self) -> float:
33+
if len(self.lo) == len(self.hi):
34+
return (-self.lo[0] + self.hi[0]) / 2
35+
else:
36+
return self.hi[0]
37+
38+
# Your MedianFinder object will be instantiated and called as such:
39+
# obj = MedianFinder()
40+
# obj.addNum(num)
41+
# param_2 = obj.findMedian()

insert-interval/seungriyou.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# https://leetcode.com/problems/insert-interval/
2+
3+
from typing import List
4+
5+
class Solution:
6+
def insert(self, intervals: List[List[int]], newInterval: List[int]) -> List[List[int]]:
7+
"""
8+
[Complexity]
9+
- TC: O(n)
10+
- SC: O(n)
11+
12+
[Approach]
13+
intervals의 각 interval에 대해 다음의 케이스로 나눠볼 수 있다.
14+
1) left에 해당하는 interval: left에 추가
15+
2) right에 해당하는 interval: right에 추가
16+
3) newInterval과 겹치는 interval: ns & ne 업데이트 (newInterval 확장)
17+
"""
18+
ns, ne = newInterval
19+
20+
# left: end < ns
21+
# right: start > ne
22+
left, right = [], []
23+
24+
for s, e in intervals:
25+
# 1) left에 해당하는 interval이라면, left에 추가
26+
if e < ns:
27+
left.append([s, e])
28+
29+
# 2) right에 해당하는 interval이라면, right에 추가
30+
elif s > ne:
31+
right.append([s, e])
32+
33+
# 3) newInterval과 겹치는 interval이라면, ns & ne 업데이트
34+
else:
35+
ns = min(ns, s)
36+
ne = max(ne, e)
37+
38+
# left.append([ns, ne])
39+
# left.extend(right)
40+
# return left
41+
return left + [[ns, ne]] + right
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# https://leetcode.com/problems/kth-smallest-element-in-a-bst/
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 kthSmallest_recur(self, root: Optional[TreeNode], k: int) -> int:
14+
"""
15+
[Complexity]
16+
- TC: O(k)
17+
- SC: O(height) (call stack)
18+
19+
[Approach]
20+
BST를 inorder로 순회하면 오름차순으로 node를 방문할 수 있다. (recursive)
21+
각 호출에서 cnt를 세며 k와 같을 때 res 값을 기록한다.
22+
"""
23+
cnt, res = 0, None
24+
25+
def inorder(node):
26+
nonlocal cnt, res
27+
28+
# base condition
29+
if not node or res:
30+
return
31+
32+
# recur
33+
inorder(node.left)
34+
cnt += 1
35+
if cnt == k:
36+
res = node.val
37+
inorder(node.right)
38+
39+
inorder(root)
40+
41+
return res
42+
43+
def kthSmallest_recur2(self, root: Optional[TreeNode], k: int) -> int:
44+
"""
45+
[Complexity]
46+
- TC: O(k)
47+
- SC: O(height) (call stack)
48+
49+
[Approach]
50+
이전 recursive 풀이를 generator 방식으로 바꿀 수 있다. (yield from으로 recursion 구현)
51+
"""
52+
53+
def inorder(node):
54+
if node:
55+
yield from inorder(node.left)
56+
yield node
57+
yield from inorder(node.right)
58+
59+
for i, node in enumerate(inorder(root), start=1):
60+
if i == k:
61+
return node.val
62+
63+
def kthSmallest(self, root: Optional[TreeNode], k: int) -> int:
64+
"""
65+
[Complexity]
66+
- TC: O(k)
67+
- SC: O(height) (stack에는 최대 height 개의 node가 들어감)
68+
69+
[Approach]
70+
BST의 inorder 순회를 stack을 이용하여 iterative 하게 풀이할 수 있다.
71+
"""
72+
cnt, stack = 0, []
73+
74+
# root에서부터 left child를 stack에 넣기
75+
while root:
76+
stack.append(root)
77+
root = root.left
78+
79+
# leaf left child 부터 stack에서 pop
80+
while stack:
81+
node = stack.pop()
82+
cnt += 1
83+
84+
if cnt == k:
85+
return node.val
86+
87+
# 현재 node의 right child가 있다면 stack에 넣고
88+
right = node.right
89+
while right:
90+
# right child에서부터 left child를 stack에 넣기
91+
stack.append(right)
92+
right = right.left
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-search-tree/
2+
3+
# Definition for a binary tree node.
4+
# class TreeNode:
5+
# def __init__(self, x):
6+
# self.val = x
7+
# self.left = None
8+
# self.right = None
9+
10+
class Solution:
11+
def lowestCommonAncestor_recur(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
12+
"""
13+
[Complexity]
14+
- TC: O(height)
15+
- SC: O(height) (call stack)
16+
17+
[Approach]
18+
어떤 node와 두 노드 p, q 간의 관계를 다음의 케이스로 나누어 볼 수 있다.
19+
1) p와 q가 모두 현재 node 보다 작다면 --> left subtree로 내려가 살펴보기
20+
2) p와 q가 모두 현재 node 보다 크다면 --> right subtree로 내려가 살펴보기
21+
3) p와 q가 현재 node의 두 child subtree에 각각 존재한다면 --> 현재 node가 p, q의 LCA
22+
"""
23+
24+
def find_lca(node):
25+
# 1) p와 q가 모두 현재 node 보다 작다면 --> left subtree로 내려가 살펴보기
26+
if p.val < node.val > q.val:
27+
return find_lca(node.left)
28+
# 2) p와 q가 모두 현재 node 보다 크다면 --> right subtree로 내려가 살펴보기
29+
if p.val > node.val < q.val:
30+
return find_lca(node.right)
31+
# 3) p와 q가 현재 node의 두 child subtree에 각각 존재한다면 --> 현재 node가 p, q의 LCA
32+
return node
33+
34+
return find_lca(root)
35+
36+
def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
37+
"""
38+
[Complexity]
39+
- TC: O(height)
40+
- SC: O(1)
41+
42+
[Approach]
43+
이전 recursive 풀이를 iterative 하게 풀이할 수 있다.
44+
"""
45+
while root:
46+
# 1) p와 q가 모두 현재 node 보다 작다면 --> left subtree로 내려가 살펴보기
47+
if p.val < root.val > q.val:
48+
root = root.left
49+
# 2) p와 q가 모두 현재 node 보다 크다면 --> right subtree로 내려가 살펴보기
50+
elif p.val > root.val < q.val:
51+
root = root.right
52+
# 3) p와 q가 현재 node의 두 child subtree에 각각 존재한다면 --> 현재 node가 p, q의 LCA
53+
else:
54+
return root

meeting-rooms/seungriyou.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# https://leetcode.com/problems/meeting-rooms/
2+
3+
from typing import List
4+
5+
class Solution:
6+
def canAttendMeetings(self, intervals: List[List[int]]) -> bool:
7+
"""
8+
[Complexity]
9+
- TC: O(nlogn)
10+
- SC: O(1) (inplace sorting)
11+
12+
[Approach]
13+
intervals를 start 기준으로 오름차순 정렬 후, 앞 회의의 끝 시간 > 뒷 회의의 시작 시간이라면 겹치는 것이므로 False 반환
14+
"""
15+
# sort intervals (by start)
16+
intervals.sort()
17+
18+
for i in range(1, len(intervals)):
19+
# prev_e > curr_s 라면 False
20+
if intervals[i - 1][1] > intervals[i][0]:
21+
return False
22+
23+
return True

0 commit comments

Comments
 (0)