diff --git a/non-overlapping-intervals/alphaorderly.py b/non-overlapping-intervals/alphaorderly.py new file mode 100644 index 0000000000..9e586ef057 --- /dev/null +++ b/non-overlapping-intervals/alphaorderly.py @@ -0,0 +1,32 @@ +""" +# 시간 복잡도: O(n log n) +# 공간 복잡도: O(1) +# +# 1. 인터벌을 끝점을 기준으로 오름차순 정렬한다. +# 2. 첫 번째 인터벌의 끝점을 기준으로 순회하면서, +# 다음 인터벌의 시작점이 현재 끝점보다 작으면 겹치는 것으로 간주하여 카운트를 증가시킨다. +# - 겹치면(count += 1) 끝점은 그대로 둔다. +# - 겹치지 않으면 끝점을 현재 인터벌의 끝점으로 업데이트한다. +# 3. 마지막에 카운트를 반환한다. +# +# 왜 최선인가? +# - 끝점이 가장 작은 것부터 남겨두면 이후로 더 많은 인터벌을 남길 수 있다. +# - 이는 최대한 많은 인터벌을 남기기 위해 최소 개수의 인터벌만 제거하게 되는 그리디 전략이다. +""" +class Solution: + def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int: + intervals.sort(key=lambda k: k[1]) + N = len(intervals) + + count = 0 + end = intervals[0][1] + + for i in range(1, N): + event_start, event_end = intervals[i] + + if event_start < end: + count += 1 + else: + end = event_end + + return count diff --git a/number-of-connected-components-in-an-undirected-graph/alphaorderly.py b/number-of-connected-components-in-an-undirected-graph/alphaorderly.py new file mode 100644 index 0000000000..45d9568f10 --- /dev/null +++ b/number-of-connected-components-in-an-undirected-graph/alphaorderly.py @@ -0,0 +1,53 @@ + +""" +# 시간 복잡도: O(n) (Union Find 연산은 경로 압축과 rank 활용으로 Amortized O(1)) +# 공간 복잡도: O(n) +# +# Union-Find(유니온 파인드, Disjoint Set Union) 알고리즘을 사용해 무방향 그래프의 연결 요소(connected components) 개수를 계산한다. +# +# 알고리즘 단계: +# 1. 각 노드는 자기 자신을 부모로 갖도록(parent 배열) 초기화한다. +# 2. 트리의 깊이(랭크, rank)를 저장하는 배열을 초기화한다. +# 3. 간선을 하나씩 확인하며 union 연산으로 두 노드를 같은 집합으로 합친다. +# - 이미 같은 집합인 경우엔 아무 작업도 하지 않는다. +# - 서로 다른 집합을 합치면, 연결 요소 개수를 1 감소시킨다. +# 4. 모든 간선을 처리하고 남은 연결 요소 개수(ans)를 반환한다. +""" +class UnionFind: + def __init__(self, n: int): + self.parent = [-1] * n + self.rank = [0] * n + + def find(self, target: int) -> int: + if self.parent[target] == -1: + return target + + self.parent[target] = self.find(self.parent[target]) + return self.parent[target] + + def union(self, a: int, b: int) -> bool: + a = self.find(a) + b = self.find(b) + + if a == b: + return False + + if self.rank[a] < self.rank[b]: + a, b = b, a + + self.parent[b] = a + if self.rank[a] == self.rank[b]: + self.rank[a] += 1 + + return True + +class Solution: + def countComponents(self, n: int, edges: List[List[int]]) -> int: + uf = UnionFind(n) + ans = n + + for a, b in edges: + if uf.union(a, b): + ans -= 1 + + return ans diff --git a/remove-nth-node-from-end-of-list/alphaorderly.py b/remove-nth-node-from-end-of-list/alphaorderly.py new file mode 100644 index 0000000000..365e3a2745 --- /dev/null +++ b/remove-nth-node-from-end-of-list/alphaorderly.py @@ -0,0 +1,28 @@ +""" +# 시간 복잡도: O(n) +# 공간 복잡도: O(1) +# +# 한 번의 순회(one pass)로 연결 리스트의 끝에서 n번째 노드를 제거하는 방법: +# 1. dummy 노드를 만들어 head의 앞에 연결한다. +# 2. forerunner 포인터를 n칸 먼저 이동시킨다. +# 3. forerunner와 lastcomer(초기: dummy)를 같이 한 칸씩 이동하며, forerunner가 끝(None)에 도달하면 lastcomer는 제거할 노드 바로 앞에 위치하게 된다. +# 4. lastcomer.next를 갱신하여 n번째 노드를 제거한다. +""" +class Solution: + def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]: + dummy = ListNode(0) + dummy.next = head + + forerunner = head + lastcomer = dummy + + for _ in range(n): + forerunner = forerunner.next + + while forerunner: + forerunner = forerunner.next + lastcomer = lastcomer.next + + lastcomer.next = lastcomer.next.next + + return dummy.next diff --git a/same-tree/alphaorderly.py b/same-tree/alphaorderly.py new file mode 100644 index 0000000000..5a5b2dbd70 --- /dev/null +++ b/same-tree/alphaorderly.py @@ -0,0 +1,22 @@ +""" +시간 복잡도 : O(n) +공간 복잡도 : O(n) + +재귀적으로 두 이진 트리의 노드를 비교하면서 값이 같은지 확인한다. +""" +class Solution: + def isSameTree(self, p: Optional[TreeNode], q: Optional[TreeNode]) -> bool: + # 두 노드가 둘다 None 이면 True 를 반환한다. + if p is q: + return True + + # 두 노드중 하나가 None 이면 False 를 반환한다. + if not (p and q): + return False + + # 둘다 None이 아니라면 값을 비교하고 왼쪽과 오른쪽 서브트리도 재귀적으로 비교한다. + return ( + p.val == q.val + and self.isSameTree(p.left, q.left) + and self.isSameTree(p.right, q.right) + ) diff --git a/serialize-and-deserialize-binary-tree/alphaorderly.py b/serialize-and-deserialize-binary-tree/alphaorderly.py new file mode 100644 index 0000000000..615bf35562 --- /dev/null +++ b/serialize-and-deserialize-binary-tree/alphaorderly.py @@ -0,0 +1,58 @@ +# Definition for a binary tree node. +# class TreeNode(object): +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +""" +# 시간 복잡도: O(n) +# 공간 복잡도: O(n) +# +# 이 코드는 preorder(전위 순회)를 이용해 이진 트리를 문자열로 직렬화하고, +# 다시 preorder 순서로 문자열을 역직렬화하여 트리를 복원한다. +# +# 1. serialize 함수는 preorder 순회로 트리 노드를 방문하며 값을 문자열로 저장하고, +# None 자리는 특수문자(*)로 표기한다. +# 2. deserialize 함수는 preorder 순서의 문자열을 한 항목씩 읽어가며 +# *이면 None, 숫자면 그 값의 TreeNode를 재귀적으로 생성한다. +""" +class Codec: + + def serialize(self, root): + ans = [] + + def preorder(node: Optional[TreeNode]): + if not node: + ans.append("*") + return + + ans.append(str(node.val)) + preorder(node.left) + preorder(node.right) + + preorder(root) + return ','.join(ans) + + def deserialize(self, data): + data = iter(data.split(',')) + + def preorder() -> TreeNode: + current = next(data) + + if current == '*': + return None + + node = TreeNode(int(current)) + node.left = preorder() + node.right = preorder() + + return node + + return preorder() + + +# Your Codec object will be instantiated and called as such: +# ser = Codec() +# deser = Codec() +# ans = deser.deserialize(ser.serialize(root))