-
Notifications
You must be signed in to change notification settings - Fork 126
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[haklee] week 12 #568
[haklee] week 12 #568
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
"""TC: O(l * i + n), SC: O(n) | ||
|
||
※ 쉬운 길을 돌아가는 풀이라는 것을 감안하고 읽어야 한다!!! | ||
※ 구간의 끝 값이 10^4라고 되어있는 것을 보고 효율 안 따지고 냅다 아래의 방법으로 접근해보았다. | ||
|
||
n은 전체 구간의 끝 값. 문제에서는 10^4으로 주어져 있다. (구체적으로는, 구간의 시작과 끝이 [0, 10^4] 구간에 존재) | ||
i는 전체 인터벌의 개수. 문제에서는 10^4으로 주어져 있다. | ||
l은 각 인터벌의 크기. 문제에서는 10^4으로 주어져 있다. | ||
|
||
아이디어: | ||
- n칸 짜리 일직선으로 되어있는 벽이 있다고 하자. 이 벽에는 아무런 칠이 되어있지 않다. | ||
- [0, 0, ..., 0] 리스트라고 생각하자. | ||
- 모든 인터벌을 순회하면서 인터벌의 시작, 끝 값을 활용하여 이 벽의 일정 구간에 페인트를 칠한다고 하자. | ||
- 특정 구간의 값을 1로 바꾼다. | ||
- [0, ..., 0, 1, ..., 1, 0, ..., 0] | ||
^ ^ | ||
s e - 1 | ||
- 페인트 칠이 끝났으면 벽의 시작부터 끝까지 훑으면서 칠해진 구간을 찾아내어 결과로 리턴한다. | ||
|
||
SC: | ||
- 벽을 길이 n짜리 리스트로 관리. O(n). | ||
- 페인트 칠이 완료된 벽에서 구간을 찾을때 구간의 시작, 끝 인덱스를 관리하는 값에서 O(1). | ||
- 종합하면 O(n). | ||
|
||
TC: | ||
- 각 인터벌을 순회할 때마다 벽 리스트에서 최대 l개의 아이템에 접근해서 값을 1로 바꾼다. O(l). | ||
- 위의 작업을 인터벌 개수 만큼 진행. 여기까지 O(l * i). | ||
- 페인트 칠이 끝난 벽을 순회하며 인터벌 추출. O(n). | ||
- 종합하면 O(l * i + n). | ||
""" | ||
|
||
|
||
class Solution: | ||
def merge(self, intervals: List[List[int]]) -> List[List[int]]: | ||
max_v = int(1e4 + 2) | ||
flags = [False] * max_v | ||
|
||
for i in intervals: | ||
for v in range(i[0], i[1] + 1): | ||
flags[v] = True | ||
|
||
res = [] | ||
|
||
make_interval = False | ||
int_s, int_e = -1, -1 | ||
|
||
for i in range(max_v): | ||
if flags[i]: | ||
# 인터벌에 포함되어야 하는 값 | ||
if not make_interval: | ||
# 인터벌의 시작 값이다. 인터벌 시작을 i로 세팅. | ||
make_interval = True | ||
int_s = i | ||
else: | ||
# 인터벌에 포함된 값이다. 인터벌 끝을 i로 세팅. | ||
int_e = i | ||
else: | ||
# 인터벌에 포함 안되는 값 | ||
if make_interval: | ||
# 직전 값까지는 인터벌에 포함되었으므로, i에서 | ||
# 인터벌이 끝났다. 인터벌을 더해줌. | ||
res.append([int_s, int_e]) | ||
make_interval = False | ||
|
||
return res | ||
|
||
|
||
""" | ||
그런데 위의 코드를 제출하면 오답이라고 나온다. 왜냐하면, 문제 조건상 인터벌의 s와 e값이 같을 수 있고, | ||
따라서 길이 0짜리 구간이 존재할 수 있기 때문!!!!! (길이 0짜리 인터벌이라니... 분노를 금할 수 없다.) | ||
그래서 위에서 벽에 페인트를 칠하는 비유로는 설명이 불가능한 이상한 인터벌도 결과에 포함시켜서 리턴해야 한다. | ||
이를 위해서 별도의 처리를 한 것이 아래의 코드다. 추가된 코드를 보기 편하게 하기 위해 한글 변수명을 활용했다. | ||
""" | ||
|
||
|
||
class Solution: | ||
def merge(self, intervals: List[List[int]]) -> List[List[int]]: | ||
max_v = int(1e4 + 2) | ||
flags = [False] * max_v | ||
억까 = [] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
for i in intervals: | ||
for v in range(i[0], i[1]): | ||
flags[v] = True | ||
if i[0] == i[1]: | ||
억까.append(i[0]) | ||
|
||
억까 = list(set(억까)) | ||
억까.sort() | ||
|
||
res = [] | ||
|
||
make_interval = False | ||
int_s, int_e = -2, -2 | ||
억까_ind = 0 | ||
|
||
for i in range(max_v): | ||
if flags[i]: | ||
# 인터벌에 포함되어야 하는 값 | ||
if not make_interval: | ||
# 길이가 있는 인터벌 앞에 오는 길이 0짜리 인터벌들을 추가해주자. | ||
while 억까_ind < len(억까) and (v := 억까[억까_ind]) <= i: | ||
if int_e + 1 < v: | ||
# 직전에 결과에 추가한 인터벌의 끝 값보다는 커야 한다. | ||
if v < i: | ||
res.append([v, v]) | ||
억까_ind += 1 | ||
|
||
# 인터벌의 시작 값이다. 인터벌의 시작과 끝을 i로 세팅. | ||
make_interval = True | ||
int_s = int_e = i | ||
|
||
else: | ||
# 인터벌에 포함된 값이다. 인터벌 끝을 i로 세팅. | ||
int_e = i | ||
else: | ||
# 인터벌에 포함 안되는 값 | ||
if make_interval: | ||
# 직전 값까지는 인터벌에 포함되었으므로, i에서 | ||
# 인터벌이 끝났다. 인터벌을 더해줌. | ||
res.append([int_s, int_e + 1]) | ||
make_interval = False | ||
|
||
while 억까_ind < len(억까): | ||
# 끝에 오는 길이 0짜리 인터벌들을 마저 추가해주자. | ||
if int_e + 1 < (v := 억까[억까_ind]): | ||
res.append([v, v]) | ||
억까_ind += 1 | ||
|
||
return res |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
"""TC: O(n), SC: O(1) | ||
|
||
n은 주어진 노드의 개수, e는 주어진 엣지의 개수. | ||
|
||
아이디어: | ||
- union-find를 활용하여 disjoint set을 찾는다. | ||
|
||
SC: | ||
- parent, rank값 관리에 각각 길이 n짜리 리스트가 필요하다. O(n). | ||
- 결과 값을 찾을때 각 인덱스마다 find 함수의 결과를 찾아서 리스트로 만들고, 이를 set으로 만들어서 | ||
길이 측정. 여기서도 O(n). | ||
- 종합하면 O(n). | ||
|
||
TC: | ||
- union, find 각각 union by rank 적용시 O(α(n)) 만큼의 시간이 든다. 이때 α(n)은 inverse Ackermann function | ||
으로, 매우 느린 속도로 늘어나므로 사실상 상수라고 봐도 무방하다. | ||
- union을 e회, find를 n회 시행하므로 O((n + e) * α(n)). | ||
- 모든 노드에 find를 시행해서 얻은 값을 set으로 만들때 리스트를 전부 순회하므로 O(n). | ||
- 종합하면 O((n + e) * α(n)). | ||
""" | ||
|
||
|
||
class Solution: | ||
""" | ||
@param n: the number of vertices | ||
@param edges: the edges of undirected graph | ||
@return: the number of connected components | ||
""" | ||
|
||
def count_components(self, n: int, edges: List[List[int]]) -> int: | ||
# write your code here | ||
|
||
# union find | ||
parent = list(range(n)) | ||
rank = [0] * n | ||
|
||
def find(x: int) -> bool: | ||
if x == parent[x]: | ||
return x | ||
|
||
parent[x] = find(parent[x]) # path-compression | ||
return parent[x] | ||
|
||
def union(a: int, b: int) -> bool: | ||
pa = find(a) | ||
pb = find(b) | ||
|
||
# union by rank | ||
if pa == pb: | ||
return | ||
|
||
if rank[pa] < rank[pb]: | ||
pa, pb = pb, pa | ||
|
||
parent[pb] = pa | ||
|
||
if rank[pa] == rank[pb]: | ||
rank[pa] += 1 | ||
|
||
for e in edges: | ||
union(*e) | ||
|
||
return len(set(find(i) for i in range(n))) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
"""TC: O(n), SC: O(1) | ||
n은 주어진 리스트의 길이. | ||
아이디어: | ||
- 길이를 먼저 측정한다. | ||
- 그 다음 제거할 노드의 인덱스를 구해서 해당 인덱스의 아이템을 제거. | ||
SC: | ||
- 리스트의 길이 값 및 리스트 탐색에 사용하는 인덱스 값을 관리. O(1). | ||
TC: | ||
- 길이 값 구할때 리스트를 전체 순회. O(n). | ||
- 특정 인덱스에 해당하는 노드 제거시 최악의 경우 끝 노드까지 탐색해야 한다. O(n). | ||
- 종합하면 O(n). | ||
""" | ||
|
||
|
||
# Definition for singly-linked list. | ||
# class ListNode: | ||
# def __init__(self, val=0, next=None): | ||
# self.val = val | ||
# self.next = next | ||
class Solution: | ||
def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]: | ||
# 길이 측정 | ||
l = 0 | ||
cur_head = head | ||
while True: | ||
if not cur_head: | ||
break | ||
l += 1 | ||
cur_head = cur_head.next | ||
|
||
# 노드 하나 제거 | ||
i = 0 | ||
dummy_head = ListNode() | ||
dummy_head.next = head | ||
cur_head = dummy_head | ||
while i != l - n: | ||
i += 1 | ||
cur_head = cur_head.next | ||
|
||
cur_head.next = None if cur_head.next is None else cur_head.next.next | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 모든 노드를 순회하지 않는 방법을 선택하셨네요. 좋은 방법인거 같아요! |
||
return dummy_head.next |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
"""TC: O(n), SC: O(h) | ||
|
||
n은 주어진 트리 p, q의 노드 개수 중 더 작은 쪽의 값. | ||
h는 주어진 트리 p의 높이. | ||
|
||
아이디어: | ||
- p를 dfs로 돌면서 q도 같은 순서로 dfs를 돌린다. | ||
- 이때 순회하다가 하나라도 다른 값이 나오면 False. 모두 같으면 True. | ||
|
||
SC: | ||
- p를 기준으로 dfs를 돌고 있으므로 호출 스택의 깊이가 p의 깊이보다 깊어질 수 없다. O(h). | ||
|
||
TC: | ||
- 최악의 경우 모든 노드 순회 후 True 리턴. O(n). | ||
- False를 리턴할 경우 트리 순회 중 멈춘다. 이 경우 두 트리 중 더 적은 노드 개수 보다 | ||
적은 회수 만큼 순회. 이 경우에도 O(n). | ||
- 종합하면 O(n). | ||
""" | ||
|
||
|
||
# Definition for a binary tree node. | ||
# class TreeNode: | ||
# def __init__(self, val=0, left=None, right=None): | ||
# self.val = val | ||
# self.left = left | ||
# self.right = right | ||
class Solution: | ||
def isSameTree(self, p: Optional[TreeNode], q: Optional[TreeNode]) -> bool: | ||
return (p is None and q is None) or ( | ||
p is not None | ||
and q is not None | ||
and p.val == q.val | ||
and self.isSameTree(p.left, q.left) | ||
and self.isSameTree(p.right, q.right) | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
"""TC: O(n), SC: O(1) | ||
|
||
n은 주어진 트리의 노드 개수. | ||
|
||
아이디어: | ||
- 트리 구조를 dict로 만들어버리자. | ||
- Node = None | {v: int, l: Node, r: Node} | ||
- 이 dict를 python에 있는 json 패키지를 써서 string으로 바꾸고, string에서 불러온다. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 오.. 정말 간단하게 생각해서 풀수도 있는 문제군요..?!👍 |
||
|
||
SC: | ||
- dict에 들어가는 정보의 크기는 노드 개수만큼 커지며, 이걸 그대로 string으로 바꾸기 때문에 | ||
노드 개수에 비례하여 증가. O(n). | ||
|
||
TC: | ||
- serialize, deserialize 과정 모두 노드 개수만큼 순회. O(n). | ||
""" | ||
|
||
# Definition for a binary tree node. | ||
# class TreeNode(object): | ||
# def __init__(self, x): | ||
# self.val = x | ||
# self.left = None | ||
# self.right = None | ||
|
||
import json | ||
|
||
|
||
class Codec: | ||
|
||
def serialize(self, root): | ||
"""Encodes a tree to a single string. | ||
|
||
:type root: TreeNode | ||
:rtype: str | ||
""" | ||
|
||
def write_node(node): | ||
d = None | ||
if node: | ||
d = { | ||
"v": node.val, | ||
"l": write_node(node.left), | ||
"r": write_node(node.right), | ||
} | ||
return d | ||
|
||
return json.dumps(write_node(root)) | ||
|
||
def deserialize(self, data): | ||
"""Decodes your encoded data to tree. | ||
|
||
:type data: str | ||
:rtype: TreeNode | ||
""" | ||
|
||
data = json.loads(data) | ||
|
||
def read_data(d): | ||
if d is None: | ||
return None | ||
|
||
node = TreeNode(d["v"]) | ||
node.left = read_data(d["l"]) | ||
node.right = read_data(d["r"]) | ||
|
||
return node | ||
|
||
return read_data(data) | ||
|
||
|
||
# Your Codec object will be instantiated and called as such: | ||
# ser = Codec() | ||
# deser = Codec() | ||
# ans = deser.deserialize(ser.serialize(root)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ㅋㅌㅋㅋㅋㅋ