-
-
Notifications
You must be signed in to change notification settings - Fork 336
[liza0525] WEEK 12 Solutions #2608
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
Changes from all commits
5f820cc
090916a
1698758
8c6dfdf
0ca9295
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,20 @@ | ||
| # 7기 풀이 | ||
| # 시간 복잡도: O(n log n) | ||
| # - sorting에 O(n log n), 이후 전체 탐색에 O(n) | ||
| # - 종합 O(n log n) | ||
| # 공간 복잡도: O(1) | ||
| # - 추가 자료구조 없이 변수만 사용 | ||
| class Solution: | ||
| def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int: | ||
| intervals.sort(key=lambda x: x[1]) # end 기준으로 구간들을 오름차순 | ||
| res = 0 | ||
|
|
||
| prev_end = intervals[0][1] # 제일 첫번째 구간의 end를 prev_end로 저장 | ||
|
|
||
| for curr_start, curr_end in intervals[1:]: # 1번 인덱스부터 loop | ||
| if curr_start < prev_end: # 이전 구간 end와 curr_start 구간이 겹칠 때 | ||
| res += 1 # res를 하나 올린다 | ||
| else: # 겹치지 않는다면 | ||
| prev_end = curr_end # 지금 구간의 end를 다음 loop의 prev_end가 되도록 할당 | ||
|
|
||
| return res |
|
Contributor
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. 🏷️ 알고리즘 패턴 분석
📊 시간/공간 복잡도 분석
피드백: 인접 리스트를 이용한 BFS 탐색으로 모든 노드와 엣지를 한 번씩 방문하므로 시간 복잡도는 노드와 엣지의 개수에 비례합니다. 방문 여부를 저장하는 배열과 그래프 표현이 공간을 차지합니다. 개선 제안: 현재 구현이 적절해 보입니다. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| from typing import List | ||
| from collections import defaultdict, deque | ||
|
|
||
|
|
||
| # 7기 풀이 | ||
| # 시간 복잡도: O(V + E) | ||
| # - 모든 노드과 연결된 엣지를 탐색하면서 component 개수를 확인하므로 노드 개수(V)와 엣지 개수(E)만큼 시간 소요 | ||
| # 공간 복잡도: O(V + E) | ||
| # - 노드의 개수(V)와 엣지의 개수(E)만큼 mappers를 만드므로 V + E 만큼 공간 사용 | ||
| class Solution: | ||
| def count_components(self, n: int, edges: List[List[int]]) -> int: | ||
| res = 0 | ||
| mappers = defaultdict(list) | ||
|
|
||
| for node1, node2 in edges: | ||
| # 무방향 그래프의 정보 저장 | ||
| mappers[node1].append(node2) | ||
| mappers[node2].append(node1) | ||
|
|
||
| visited = [0 for _ in range(n)] | ||
|
|
||
| def bfs(node): | ||
| # bfs로 어떤 node들이 연결되어 있는 지 탐색한다. | ||
| queue = deque([node]) | ||
|
|
||
| while queue: # queue에 요소가 있는 동안은 계속 탐색 | ||
| node = queue.popleft() # queue에서 가장 빠른 요소 pop | ||
| visited[node] = 1 # 해당 노드는 방문 처리 | ||
|
|
||
| next_nodes = mappers.get(node, []) # mappers에서 다음 노드 정보 가져오기 | ||
| for next_node in next_nodes: | ||
| if visited[next_node]: | ||
| # 다음 노드가 이미 방문한 노드라면 탐색하지 않고 스킵해도 됨 | ||
| continue | ||
| # 다음 노드를 queue에 저장하여 다음 탐색 때 이용 | ||
| queue.append((next_node)) | ||
|
|
||
|
|
||
| for node in range(n): | ||
| if visited[node]: | ||
| # 이미 방문 처리가 되었다면 해당 노드는 이전에 탐색 완료되었다는 의미 | ||
| # bfs 탐색의 시작점으로 사용하지 않고 skip한다 | ||
| continue | ||
|
|
||
| # bfs 탐색 | ||
| bfs(node) | ||
|
|
||
| # 탐색을 마쳤다면 component 하나를 찾았다는 의미로 res를 하나 올린다. | ||
| res += 1 | ||
|
|
||
| return res |
|
Contributor
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. 🏷️ 알고리즘 패턴 분석
📊 시간/공간 복잡도 분석
피드백: 단일 순회와 재귀 호출로 뒤에서 n번째 노드를 찾으며, 재귀 스택이 노드 수만큼 쌓입니다. 시간은 노드 수에 비례합니다. 개선 제안: 현재 구현이 적절해 보입니다. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| # 7기 풀이 | ||
| # 시간 복잡도: O(n) | ||
| # - 마지막 노드까지, 모든 노드를 탐색해야 하므로 노드의 개수에 시간 복잡도가 정해짐 | ||
| # 공간 복잡도: O(n) | ||
| # - 재귀 스택이 노드의 개수만큼 쌓임 | ||
| class Solution: | ||
| def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]: | ||
| # dummy 노드와 next node 설정 및 curr_node로 포인터 지정 | ||
| dummy = ListNode() | ||
| dummy.next = head | ||
| curr_node = head | ||
|
|
||
| def dfs(parent, node): | ||
| if not node: | ||
| # 끝에 도달했으므로 카운팅 시작점 0을 return | ||
| # 호출부에서 +1을 하면 마지막 노드가 1이 됨 | ||
| return 0 | ||
|
|
||
| curr_i = dfs(node, node.next) + 1 | ||
|
|
||
| # (뒤로부터 셌을 때의) 현재 노드의 순번이 주어진 n과 같을 때 | ||
| if curr_i == n: | ||
| # 부모 노드의 next 노드를, 현재 노드의 next 노드로 변경하여 | ||
| # 현재 노드의 연결을 끊는다. | ||
| parent.next = node.next | ||
|
|
||
| return curr_i | ||
|
|
||
| dfs(dummy, curr_node) | ||
| return dummy.next |
|
Contributor
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. 🏷️ 알고리즘 패턴 분석
📊 시간/공간 복잡도 분석
피드백: 모든 노드를 한 번씩 방문하며, 스택에 최대 트리의 너비만큼 쌓이므로 공간 복잡도는 너비에 비례합니다. 시간은 노드 수에 비례합니다. 개선 제안: 현재 구현이 적절해 보입니다. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| # 7기 풀이 | ||
| # 시간 복잡도: O(n) | ||
| # - 두 트리의 노드 개수가 동일할 때 모든 노드를 탐색하므로 O(n) | ||
| # - 다를 경우 early return으로 그보다 적게 탐색 | ||
| # 공간 복잡도: O(n) | ||
| # - stack에는 최대 트리의 너비(width)만큼 pair가 쌓임 | ||
| class Solution: | ||
| def isSameTree(self, p: Optional[TreeNode], q: Optional[TreeNode]) -> bool: | ||
| stack = [(p, q)] # p와 q를 tuple의 형태로 stack에 저장 | ||
|
|
||
| while stack: # stack의 요소가 다 없어질 때까지 트리 탐색 | ||
| p_node, q_node = stack.pop() # node 꺼내기 | ||
|
|
||
| if not p_node and not q_node: | ||
| # 둘 다 없는 경우(null인 경우)는 leaf node로부터 온 것이기 때문에 | ||
| # 더이상 탐색을 안해도 됨 | ||
| continue | ||
| elif ( | ||
| not p_node and q_node | ||
| or p_node and not q_node | ||
| ): | ||
| # 둘 중 하나만 null인 경우에는 tree가 같지 않으므로 | ||
| # False로 early return | ||
| return False | ||
| elif p_node.val != q_node.val: | ||
| # 둘 다 있는데 값이 같지 않은 경우도 | ||
| # False로 early return | ||
| return False | ||
|
|
||
| # 두 트리의 왼쪽 쌍과 오른쪽 쌍을 각각 stack에 저장 | ||
| stack.append((p_node.left, q_node.left)) | ||
| stack.append((p_node.right, q_node.right)) | ||
|
|
||
| # 모든 loop를 다 돌았다면 두 트리가 같다는 의미로 | ||
| # True로 return | ||
| return True |
|
Contributor
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. 🏷️ 알고리즘 패턴 분석
📊 시간/공간 복잡도 분석
풀이 1:
|
| 복잡도 | |
|---|---|
| Time | O(n) |
| Space | O(n) |
피드백: 모든 노드를 방문하여 값을 리스트에 저장하므로 시간과 공간 모두 노드 수에 비례합니다.
개선 제안: 현재 구현이 적절해 보입니다.
풀이 2: Codec.deserialize — Time: O(n) / Space: O(n)
| 복잡도 | |
|---|---|
| Time | O(n) |
| Space | O(n) |
피드백: 모든 노드를 재귀적으로 생성하며, 문자열을 순차적으로 처리하므로 시간과 공간 모두 노드 수에 비례합니다.
개선 제안: 현재 구현이 적절해 보입니다.
💡 풀이에 시간/공간 복잡도를 주석으로 남겨보세요!
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.
이 문제는 엄격한 조건이 없다보니 각자 구현을 보는 재미가 있는 것 같습니다👀
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.
오 그렇네요! 풀이 구경해보고 왔는데 BFS로 푸셨군요 :) 잘 보았습니다~!
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| from collections import deque | ||
|
|
||
| # 7기 풀이 | ||
| # 시간 복잡도: O(n) | ||
| # - 모든 노드를 순회하여 serialize / deserialize 하므로 노드의 개수만큼 시간 소요 | ||
| # 공간 복잡도: O(n) | ||
| # - 모든 노드에 대한 값을 list에 넣어 사용하므로 노드의 개수만큼 공간 사용 | ||
| class Codec: | ||
| DELIMITER = "$" # serialize할 때 노드의 값을 구분하기 위한 구분자 | ||
| NONE_VALUE = "N" # serialize할 때 노드가 None인 경우를 구분하기 위한 구분자 | ||
|
|
||
| def serialize(self, root): | ||
| """Encodes a tree to a single string. | ||
|
|
||
| :type root: TreeNode | ||
| :rtype: str | ||
| """ | ||
| data = [] # node의 값들을 저장할 list 추가 | ||
|
|
||
| def dfs(node): | ||
| if not node: | ||
| # node가 None인 경우에는 None 구분자를 저장 | ||
| data.append(self.NONE_VALUE) | ||
| return | ||
|
|
||
| data.append(str(node.val)) # node가 있는 경우에 val를 data에 저장 | ||
|
|
||
| dfs(node.left) # 왼쪽 자식 노드 탐색 | ||
| dfs(node.right) # 오른쪽 자식 노드 탐색 | ||
|
|
||
| dfs(root) # 전체 트리 탐색 | ||
| return f"{self.DELIMITER}".join(data) # 탐색하여 저장한 data list를 구분자를 이용해 string으로 변환하여 return | ||
|
|
||
| def deserialize(self, data): | ||
| """Decodes your encoded data to tree. | ||
|
|
||
| :type data: str | ||
| :rtype: TreeNode | ||
| """ | ||
|
|
||
| data = deque(data.split(self.DELIMITER)) # string 값을 구분자를 기준으로 split 한 후 deque에 저장 | ||
|
|
||
| def dfs(): | ||
| val = data.popleft() # data 값 중 가장 왼쪽에 있는 값을 pop | ||
| if val == self.NONE_VALUE: | ||
| # None 구분자의 값이 나온 경우에는 node가 없었던 것이므로 None 리턴 | ||
| return None | ||
|
|
||
| node = TreeNode(val) # 노드 생성 | ||
| node.left = dfs() # 왼쪽 자식 노드 생성 | ||
| node.right = dfs() # 오른쪽 자식 노드 생성 | ||
|
|
||
| return node # 완성한 노드 return | ||
|
|
||
| root = dfs() | ||
| return 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.
🏷️ 알고리즘 패턴 분석
📊 시간/공간 복잡도 분석
피드백: 구간을 끝나는 지점 기준으로 정렬한 후, 이전 구간의 종료점과 비교하여 겹치는 구간을 세는 방식으로 효율적입니다. 정렬이 시간 복잡도의 주된 비용입니다.
개선 제안: 현재 구현이 적절해 보입니다.