diff --git a/merge-intervals/EGON.py b/merge-intervals/EGON.py new file mode 100644 index 000000000..8dea1ef97 --- /dev/null +++ b/merge-intervals/EGON.py @@ -0,0 +1,57 @@ +from typing import List +from unittest import TestCase, main + + +class Solution: + def merge(self, intervals: List[List[int]]) -> List[List[int]]: + return self.solve_stack(intervals) + + """ + Runtime: 8 ms (Beats 49.77%) + Time Complexity: + - intervals의 길이를 n이라 하면, intervals를 시작 지점을 기준으로 정렬하는데 O(n log n) + - intervals를 조회하면서 연산하는데, 내부 연산은 모두 O(1)이므로 O(n) + > O(n log n) + O(n) ~= O(n log n) + + Memory: 19.88 MB (Beats: 99.31%) + Space Complexity: O(n) + - intervals는 내부 정렬만 했으므로 추가 메모리 사용 메모리 없음 + - 겹치는 구간이 없는 최악의 경우 merged의 크기는 intervals의 크기와 같아지므로, O(n) upper bound + """ + + def solve_stack(self, intervals: List[List[int]]) -> List[List[int]]: + intervals.sort() + + merged = [] + for interval in intervals: + if not merged: + merged.append(interval) + continue + + new_start, new_end = interval + _, last_end = merged[-1] + if last_end < new_start: + merged.append(interval) + else: + merged[-1][1] = max(last_end, new_end) + + return merged + + +class _LeetCodeTestCases(TestCase): + + def test_1(self): + intervals = [[1, 3], [2, 6], [8, 10], [15, 18]] + output = [[1, 6], [8, 10], [15, 18]] + + self.assertEqual(Solution().merge(intervals), output) + + def test_2(self): + intervals = [[1, 4], [4, 5]] + output = [[1, 5]] + + self.assertEqual(Solution().merge(intervals), output) + + +if __name__ == '__main__': + main() diff --git a/number-of-connected-components-in-an-undirected-graph/EGON.py b/number-of-connected-components-in-an-undirected-graph/EGON.py new file mode 100644 index 000000000..0d5085541 --- /dev/null +++ b/number-of-connected-components-in-an-undirected-graph/EGON.py @@ -0,0 +1,77 @@ +from typing import List +from unittest import TestCase, main + + +class ListNode: + def __init__(self, val=0, next=None): + self.val = val + self.next = next + + +class Solution: + def countComponents(self, n: int, edges: List[List[int]]) -> int: + return self.solve_union_find(n, edges) + + """ + LintCode 로그인이 안되어서 https://neetcode.io/problems/count-connected-components에서 실행시키고 통과만 확인했습니다. + + Runtime: ? ms (Beats ?%) + Time Complexity: O(max(m, n)) + - UnionFind의 parent 생성에 O(n) + - edges 조회에 O(m) + - Union-find 알고리즘의 union을 매 조회마다 사용하므로, * O(α(n)) (α는 아커만 함수의 역함수) + - UnionFind의 각 노드의 부모를 찾기 위해, n번 find에 O(n * α(n)) + > O(n) + O(m * α(n)) + O(n * α(n)) ~= O(max(m, n) * α(n)) ~= O(max(m, n)) (∵ α(n) ~= C) + + Memory: ? MB (Beats ?%) + Space Complexity: O(n) + - UnionFind의 parent와 rank가 크기가 n인 리스트이므로, O(n) + O(n) + > O(n) + O(n) ~= O(n) + """ + + def solve_union_find(self, n: int, edges: List[List[int]]) -> int: + + class UnionFind: + def __init__(self, size: int): + self.parent = [i for i in range(size)] + self.rank = [1] * size + + def union(self, first: int, second: int): + first_parent, second_parent = self.find(first), self.find(second) + + if self.rank[first_parent] > self.rank[second_parent]: + self.parent[second_parent] = first_parent + elif self.rank[first_parent] < self.rank[second_parent]: + self.parent[first_parent] = second_parent + else: + self.parent[second_parent] = first_parent + self.rank[first_parent] += 1 + + def find(self, node: int): + if self.parent[node] != node: + self.parent[node] = self.find(self.parent[node]) + + return self.parent[node] + + union_find = UnionFind(size=n) + for first, second in edges: + union_find.union(first, second) + + result = set() + for i in range(n): + result.add(union_find.find(i)) + + return len(result) + + +class _LeetCodeTestCases(TestCase): + def test_1(self): + n = 5 + edges = [[0, 1], [1, 2], [2, 3], [1, 3], [1, 4]] + output = False + + self.assertEqual(Solution().countComponents(n, edges), output) + + +if __name__ == '__main__': + main() diff --git a/remove-nth-node-from-end-of-list/EGON.py b/remove-nth-node-from-end-of-list/EGON.py new file mode 100644 index 000000000..6646d1eca --- /dev/null +++ b/remove-nth-node-from-end-of-list/EGON.py @@ -0,0 +1,91 @@ +from typing import Optional +from unittest import TestCase, main + + +# Definition for singly-linked list. +class ListNode: + def __init__(self, val=0, next=None): + self.val = val + self.next = next + + def values(self) -> [int]: + result = [] + node = self + while node: + result.append(node.val) + node = node.next + + return result + + +class Solution: + def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]: + return self.solve_two_pointer(head, n) + """ + Runtime: 0 ms (Beats 100.00%) + Time Complexity: O(n) + > list의 모든 node + dummy node를 탐색하므로 O(n + 1) ~= O(n) + + Memory: 16.62 MB (Beats 15.78%) + Space Complexity: O(1) + > 포인터만 사용하므로 O(1) + """ + def solve_two_pointer(self, head: Optional[ListNode], n: int) -> Optional[ListNode]: + if head.next is None: + return None + + dummy = ListNode(-1) + dummy.next = head + head = dummy + + slow = fast = head + while n: + if fast.next: + fast = fast.next + n -= 1 + + while fast is not None: + if fast.next is None: + slow.next = slow.next.next + break + else: + slow = slow.next + fast = fast.next + + return head.next + + +class _LeetCodeTestCases(TestCase): + def test_1(self): + node_1 = ListNode(1) + node_2 = ListNode(2) + node_3 = ListNode(3) + node_4 = ListNode(4) + node_5 = ListNode(5) + + node_1.next = node_2 + node_2.next = node_3 + node_3.next = node_4 + node_4.next = node_5 + + n = 2 + output = [1, 2, 3, 5] + self.assertEqual(Solution().removeNthFromEnd(node_1, n).values(), output) + + def test_2(self): + node_1 = ListNode(1) + n = 1 + output = [] + self.assertEqual(Solution().removeNthFromEnd(node_1, n).values(), output) + + def test_3(self): + node_1 = ListNode(1) + node_2 = ListNode(2) + node_1.next = node_2 + n = 2 + output = [2] + self.assertEqual(Solution().removeNthFromEnd(node_1, n).values(), output) + + +if __name__ == '__main__': + main() diff --git a/same-tree/EGON.py b/same-tree/EGON.py new file mode 100644 index 000000000..a9ee28701 --- /dev/null +++ b/same-tree/EGON.py @@ -0,0 +1,57 @@ +from typing import Optional +from unittest import TestCase, main + + +# 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 self.solve_dfs(p, q) + + """ + Runtime: 0 ms (Beats 100.00%) + Time Complexity: O(min(p, q)) + > dfs를 통해 모든 node를 방문하므로, 각 트리의 node의 갯수를 각각 p, q라 하면, O(min(p, q)) + + Memory: 16.62 MB (Beats 15.78%) + Space Complexity: O(min(p, q)) + > 일반적인 경우 트리의 깊이만큼 dfs 호출 스택이 쌓이나, 최악의 경우 한쪽으로 편향되었다면 O(min(p, q)), upper bound + """ + def solve_dfs(self, p: Optional[TreeNode], q: Optional[TreeNode]) -> bool: + + def dfs(p: Optional[TreeNode], q: Optional[TreeNode]) -> bool: + if p is None and q is None: + return True + elif (p is not None and q is not None) and (p.val == q.val): + return dfs(p.left, q.left) and dfs(p.right, q.right) + else: + return False + + return dfs(p, q) + + +class _LeetCodeTestCases(TestCase): + def test_1(self): + p_1 = TreeNode(1) + p_2 = TreeNode(2) + p_3 = TreeNode(3) + p_1.left = p_2 + p_1.right = p_3 + + q_1 = TreeNode(1) + q_2 = TreeNode(3) + q_3 = TreeNode(3) + q_1.left = q_2 + q_1.right = q_3 + + self.assertEqual(Solution().isSameTree(p_1, q_1), True) + + +if __name__ == '__main__': + main() diff --git a/serialize-and-deserialize-binary-tree/EGON.py b/serialize-and-deserialize-binary-tree/EGON.py new file mode 100644 index 000000000..fd863fe25 --- /dev/null +++ b/serialize-and-deserialize-binary-tree/EGON.py @@ -0,0 +1,65 @@ +from typing import Optional + + +# 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 Codec: + """ + Runtime: 76 ms (Beats 85.72%) + Time Complexity: O(n) + > dfs를 통해 모든 node를 방문하므로, O(n) + + Memory: 21.40 MB (Beats 12.82%) + Space Complexity: O(n) + > 일반적인 경우 트리의 깊이만큼 dfs 호출 스택이 쌓이나, 최악의 경우 한쪽으로 편향되었다면 트리의 깊이가 n이 될 수 있으므로 O(n), upper bound + """ + + def serialize(self, root): + """Encodes a tree to a single string. + + :type root: TreeNode + :rtype: str + """ + + def dfs(node: Optional[TreeNode]) -> None: + if node is None: + result.append("null") + return + result.append(str(node.val)) + dfs(node.left) + dfs(node.right) + + result = [] + dfs(root) + return ','.join(result) + + def deserialize(self, data): + """Decodes your encoded data to tree. + + :type data: str + :rtype: TreeNode + """ + + def dfs() -> Optional[TreeNode]: + val = next(values) + if val == "null": + return None + node = TreeNode(int(val)) + node.left = dfs() + node.right = dfs() + return node + + values = iter(data.split(',')) + return dfs() + + +# Your Codec object will be instantiated and called as such: +# ser = Codec() +# deser = Codec() +# ans = deser.deserialize(ser.serialize(root))