diff --git a/non-overlapping-intervals/liza0525.py b/non-overlapping-intervals/liza0525.py new file mode 100644 index 0000000000..de6ed8d642 --- /dev/null +++ b/non-overlapping-intervals/liza0525.py @@ -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 diff --git a/number-of-connected-components-in-an-undirected-graph/liza0525.py b/number-of-connected-components-in-an-undirected-graph/liza0525.py new file mode 100644 index 0000000000..89002a15c3 --- /dev/null +++ b/number-of-connected-components-in-an-undirected-graph/liza0525.py @@ -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 diff --git a/remove-nth-node-from-end-of-list/liza0525.py b/remove-nth-node-from-end-of-list/liza0525.py new file mode 100644 index 0000000000..245979f127 --- /dev/null +++ b/remove-nth-node-from-end-of-list/liza0525.py @@ -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 diff --git a/same-tree/liza0525.py b/same-tree/liza0525.py new file mode 100644 index 0000000000..b442bdd6c6 --- /dev/null +++ b/same-tree/liza0525.py @@ -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 diff --git a/serialize-and-deserialize-binary-tree/liza0525.py b/serialize-and-deserialize-binary-tree/liza0525.py new file mode 100644 index 0000000000..b258fd468e --- /dev/null +++ b/serialize-and-deserialize-binary-tree/liza0525.py @@ -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