Skip to content

Commit 611e335

Browse files
committed
solutions
1 parent ba77b9c commit 611e335

File tree

5 files changed

+347
-0
lines changed

5 files changed

+347
-0
lines changed

merge-intervals/haklee.py

+130
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
"""TC: O(l * i + n), SC: O(n)
2+
3+
※ 쉬운 길을 돌아가는 풀이라는 것을 감안하고 읽어야 한다!!!
4+
※ 구간의 끝 값이 10^4라고 되어있는 것을 보고 효율 안 따지고 냅다 아래의 방법으로 접근해보았다.
5+
6+
n은 전체 구간의 끝 값. 문제에서는 10^4으로 주어져 있다. (구체적으로는, 구간의 시작과 끝이 [0, 10^4] 구간에 존재)
7+
i는 전체 인터벌의 개수. 문제에서는 10^4으로 주어져 있다.
8+
l은 각 인터벌의 크기. 문제에서는 10^4으로 주어져 있다.
9+
10+
아이디어:
11+
- n칸 짜리 일직선으로 되어있는 벽이 있다고 하자. 이 벽에는 아무런 칠이 되어있지 않다.
12+
- [0, 0, ..., 0] 리스트라고 생각하자.
13+
- 모든 인터벌을 순회하면서 인터벌의 시작, 끝 값을 활용하여 이 벽의 일정 구간에 페인트를 칠한다고 하자.
14+
- 특정 구간의 값을 1로 바꾼다.
15+
- [0, ..., 0, 1, ..., 1, 0, ..., 0]
16+
^ ^
17+
s e - 1
18+
- 페인트 칠이 끝났으면 벽의 시작부터 끝까지 훑으면서 칠해진 구간을 찾아내어 결과로 리턴한다.
19+
20+
SC:
21+
- 벽을 길이 n짜리 리스트로 관리. O(n).
22+
- 페인트 칠이 완료된 벽에서 구간을 찾을때 구간의 시작, 끝 인덱스를 관리하는 값에서 O(1).
23+
- 종합하면 O(n).
24+
25+
TC:
26+
- 각 인터벌을 순회할 때마다 벽 리스트에서 최대 l개의 아이템에 접근해서 값을 1로 바꾼다. O(l).
27+
- 위의 작업을 인터벌 개수 만큼 진행. 여기까지 O(l * i).
28+
- 페인트 칠이 끝난 벽을 순회하며 인터벌 추출. O(n).
29+
- 종합하면 O(l * i + n).
30+
"""
31+
32+
33+
class Solution:
34+
def merge(self, intervals: List[List[int]]) -> List[List[int]]:
35+
max_v = int(1e4 + 2)
36+
flags = [False] * max_v
37+
38+
for i in intervals:
39+
for v in range(i[0], i[1] + 1):
40+
flags[v] = True
41+
42+
res = []
43+
44+
make_interval = False
45+
int_s, int_e = -1, -1
46+
47+
for i in range(max_v):
48+
if flags[i]:
49+
# 인터벌에 포함되어야 하는 값
50+
if not make_interval:
51+
# 인터벌의 시작 값이다. 인터벌 시작을 i로 세팅.
52+
make_interval = True
53+
int_s = i
54+
else:
55+
# 인터벌에 포함된 값이다. 인터벌 끝을 i로 세팅.
56+
int_e = i
57+
else:
58+
# 인터벌에 포함 안되는 값
59+
if make_interval:
60+
# 직전 값까지는 인터벌에 포함되었으므로, i에서
61+
# 인터벌이 끝났다. 인터벌을 더해줌.
62+
res.append([int_s, int_e])
63+
make_interval = False
64+
65+
return res
66+
67+
68+
"""
69+
그런데 위의 코드를 제출하면 오답이라고 나온다. 왜냐하면, 문제 조건상 인터벌의 s와 e값이 같을 수 있고,
70+
따라서 길이 0짜리 구간이 존재할 수 있기 때문!!!!! (길이 0짜리 인터벌이라니... 분노를 금할 수 없다.)
71+
그래서 위에서 벽에 페인트를 칠하는 비유로는 설명이 불가능한 이상한 인터벌도 결과에 포함시켜서 리턴해야 한다.
72+
이를 위해서 별도의 처리를 한 것이 아래의 코드다. 추가된 코드를 보기 편하게 하기 위해 한글 변수명을 활용했다.
73+
"""
74+
75+
76+
class Solution:
77+
def merge(self, intervals: List[List[int]]) -> List[List[int]]:
78+
max_v = int(1e4 + 2)
79+
flags = [False] * max_v
80+
억까 = []
81+
82+
for i in intervals:
83+
for v in range(i[0], i[1]):
84+
flags[v] = True
85+
if i[0] == i[1]:
86+
억까.append(i[0])
87+
88+
억까 = list(set(억까))
89+
억까.sort()
90+
91+
res = []
92+
93+
make_interval = False
94+
int_s, int_e = -2, -2
95+
억까_ind = 0
96+
97+
for i in range(max_v):
98+
if flags[i]:
99+
# 인터벌에 포함되어야 하는 값
100+
if not make_interval:
101+
# 길이가 있는 인터벌 앞에 오는 길이 0짜리 인터벌들을 추가해주자.
102+
while 억까_ind < len(억까) and (v := 억까[억까_ind]) <= i:
103+
if int_e + 1 < v:
104+
# 직전에 결과에 추가한 인터벌의 끝 값보다는 커야 한다.
105+
if v < i:
106+
res.append([v, v])
107+
억까_ind += 1
108+
109+
# 인터벌의 시작 값이다. 인터벌의 시작과 끝을 i로 세팅.
110+
make_interval = True
111+
int_s = int_e = i
112+
113+
else:
114+
# 인터벌에 포함된 값이다. 인터벌 끝을 i로 세팅.
115+
int_e = i
116+
else:
117+
# 인터벌에 포함 안되는 값
118+
if make_interval:
119+
# 직전 값까지는 인터벌에 포함되었으므로, i에서
120+
# 인터벌이 끝났다. 인터벌을 더해줌.
121+
res.append([int_s, int_e + 1])
122+
make_interval = False
123+
124+
while 억까_ind < len(억까):
125+
# 끝에 오는 길이 0짜리 인터벌들을 마저 추가해주자.
126+
if int_e + 1 < (v := 억까[억까_ind]):
127+
res.append([v, v])
128+
억까_ind += 1
129+
130+
return res
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
"""TC: O(n), SC: O(1)
2+
3+
n은 주어진 노드의 개수, e는 주어진 엣지의 개수.
4+
5+
아이디어:
6+
- union-find를 활용하여 disjoint set을 찾는다.
7+
8+
SC:
9+
- parent, rank값 관리에 각각 길이 n짜리 리스트가 필요하다. O(n).
10+
- 결과 값을 찾을때 각 인덱스마다 find 함수의 결과를 찾아서 리스트로 만들고, 이를 set으로 만들어서
11+
길이 측정. 여기서도 O(n).
12+
- 종합하면 O(n).
13+
14+
TC:
15+
- union, find 각각 union by rank 적용시 O(α(n)) 만큼의 시간이 든다. 이때 α(n)은 inverse Ackermann function
16+
으로, 매우 느린 속도로 늘어나므로 사실상 상수라고 봐도 무방하다.
17+
- union을 e회, find를 n회 시행하므로 O((n + e) * α(n)).
18+
- 모든 노드에 find를 시행해서 얻은 값을 set으로 만들때 리스트를 전부 순회하므로 O(n).
19+
- 종합하면 O((n + e) * α(n)).
20+
"""
21+
22+
23+
class Solution:
24+
"""
25+
@param n: the number of vertices
26+
@param edges: the edges of undirected graph
27+
@return: the number of connected components
28+
"""
29+
30+
def count_components(self, n: int, edges: List[List[int]]) -> int:
31+
# write your code here
32+
33+
# union find
34+
parent = list(range(n))
35+
rank = [0] * n
36+
37+
def find(x: int) -> bool:
38+
if x == parent[x]:
39+
return x
40+
41+
parent[x] = find(parent[x]) # path-compression
42+
return parent[x]
43+
44+
def union(a: int, b: int) -> bool:
45+
pa = find(a)
46+
pb = find(b)
47+
48+
# union by rank
49+
if pa == pb:
50+
return
51+
52+
if rank[pa] < rank[pb]:
53+
pa, pb = pb, pa
54+
55+
parent[pb] = pa
56+
57+
if rank[pa] == rank[pb]:
58+
rank[pa] += 1
59+
60+
for e in edges:
61+
union(*e)
62+
63+
return len(set(find(i) for i in range(n)))
+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
"""TC: O(n), SC: O(1)
2+
3+
n은 주어진 리스트의 길이.
4+
5+
아이디어:
6+
- 길이를 먼저 측정한다.
7+
- 그 다음 제거할 노드의 인덱스를 구해서 해당 인덱스의 아이템을 제거.
8+
9+
SC:
10+
- 리스트의 길이 값 및 리스트 탐색에 사용하는 인덱스 값을 관리. O(1).
11+
12+
TC:
13+
- 길이 값 구할때 리스트를 전체 순회. O(n).
14+
- 특정 인덱스에 해당하는 노드 제거시 최악의 경우 끝 노드까지 탐색해야 한다. O(n).
15+
- 종합하면 O(n).
16+
"""
17+
18+
19+
# Definition for singly-linked list.
20+
# class ListNode:
21+
# def __init__(self, val=0, next=None):
22+
# self.val = val
23+
# self.next = next
24+
class Solution:
25+
def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
26+
# 길이 측정
27+
l = 0
28+
cur_head = head
29+
while True:
30+
if not cur_head:
31+
break
32+
l += 1
33+
cur_head = cur_head.next
34+
35+
# 노드 하나 제거
36+
i = 0
37+
dummy_head = ListNode()
38+
dummy_head.next = head
39+
cur_head = dummy_head
40+
while i != l - n:
41+
i += 1
42+
cur_head = cur_head.next
43+
44+
cur_head.next = None if cur_head.next is None else cur_head.next.next
45+
return dummy_head.next

same-tree/haklee.py

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
"""TC: O(n), SC: O(h)
2+
3+
n은 주어진 트리 p, q의 노드 개수 중 더 작은 쪽의 값.
4+
h는 주어진 트리 p의 높이.
5+
6+
아이디어:
7+
- p를 dfs로 돌면서 q도 같은 순서로 dfs를 돌린다.
8+
- 이때 순회하다가 하나라도 다른 값이 나오면 False. 모두 같으면 True.
9+
10+
SC:
11+
- p를 기준으로 dfs를 돌고 있으므로 호출 스택의 깊이가 p의 깊이보다 깊어질 수 없다. O(h).
12+
13+
TC:
14+
- 최악의 경우 모든 노드 순회 후 True 리턴. O(n).
15+
- False를 리턴할 경우 트리 순회 중 멈춘다. 이 경우 두 트리 중 더 적은 노드 개수 보다
16+
적은 회수 만큼 순회. 이 경우에도 O(n).
17+
- 종합하면 O(n).
18+
"""
19+
20+
21+
# Definition for a binary tree node.
22+
# class TreeNode:
23+
# def __init__(self, val=0, left=None, right=None):
24+
# self.val = val
25+
# self.left = left
26+
# self.right = right
27+
class Solution:
28+
def isSameTree(self, p: Optional[TreeNode], q: Optional[TreeNode]) -> bool:
29+
return (p is None and q is None) or (
30+
p is not None
31+
and q is not None
32+
and p.val == q.val
33+
and self.isSameTree(p.left, q.left)
34+
and self.isSameTree(p.right, q.right)
35+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
"""TC: O(n), SC: O(1)
2+
3+
n은 주어진 트리의 노드 개수.
4+
5+
아이디어:
6+
- 트리 구조를 dict로 만들어버리자.
7+
- Node = None | {v: int, l: Node, r: Node}
8+
- 이 dict를 python에 있는 json 패키지를 써서 string으로 바꾸고, string에서 불러온다.
9+
10+
SC:
11+
- dict에 들어가는 정보의 크기는 노드 개수만큼 커지며, 이걸 그대로 string으로 바꾸기 때문에
12+
노드 개수에 비례하여 증가. O(n).
13+
14+
TC:
15+
- serialize, deserialize 과정 모두 노드 개수만큼 순회. O(n).
16+
"""
17+
18+
# Definition for a binary tree node.
19+
# class TreeNode(object):
20+
# def __init__(self, x):
21+
# self.val = x
22+
# self.left = None
23+
# self.right = None
24+
25+
import json
26+
27+
28+
class Codec:
29+
30+
def serialize(self, root):
31+
"""Encodes a tree to a single string.
32+
33+
:type root: TreeNode
34+
:rtype: str
35+
"""
36+
37+
def write_node(node):
38+
d = None
39+
if node:
40+
d = {
41+
"v": node.val,
42+
"l": write_node(node.left),
43+
"r": write_node(node.right),
44+
}
45+
return d
46+
47+
return json.dumps(write_node(root))
48+
49+
def deserialize(self, data):
50+
"""Decodes your encoded data to tree.
51+
52+
:type data: str
53+
:rtype: TreeNode
54+
"""
55+
56+
data = json.loads(data)
57+
58+
def read_data(d):
59+
if d is None:
60+
return None
61+
62+
node = TreeNode(d["v"])
63+
node.left = read_data(d["l"])
64+
node.right = read_data(d["r"])
65+
66+
return node
67+
68+
return read_data(data)
69+
70+
71+
# Your Codec object will be instantiated and called as such:
72+
# ser = Codec()
73+
# deser = Codec()
74+
# ans = deser.deserialize(ser.serialize(root))

0 commit comments

Comments
 (0)