From 4f95930b41df75189014bea952f059baec1d3a20 Mon Sep 17 00:00:00 2001 From: "changmuk.im" Date: Wed, 2 Oct 2024 02:43:45 +0900 Subject: [PATCH] feat : solve with python --- clone-graph/EGON.py | 58 ++++++++++++++ longest-common-subsequence/EGON.py | 44 +++++++++++ .../EGON.py | 48 +++++++++++ merge-two-sorted-lists/EGON.py | 79 +++++++++++++++++++ sum-of-two-integers/EGON.py | 41 ++++++++++ 5 files changed, 270 insertions(+) create mode 100644 clone-graph/EGON.py create mode 100644 longest-common-subsequence/EGON.py create mode 100644 longest-repeating-character-replacement/EGON.py create mode 100644 merge-two-sorted-lists/EGON.py create mode 100644 sum-of-two-integers/EGON.py diff --git a/clone-graph/EGON.py b/clone-graph/EGON.py new file mode 100644 index 00000000..09bd2f10 --- /dev/null +++ b/clone-graph/EGON.py @@ -0,0 +1,58 @@ +from typing import Optional, TypeVar +from unittest import TestCase, main + + +# Definition for a Node. +class Node: + def __init__(self, val = 0, neighbors = None): + self.val = val + self.neighbors = neighbors if neighbors is not None else [] + + +class Solution: + def cloneGraph(self, node: Optional[Node]) -> Optional[Node]: + return self.solve(node) + + """ + Runtime: 42 ms (Beats 45.48%) + Time Complexity: O(n) + + Memory: 17.04 (Beats 8.15%) + Space Complexity: O(n), upper bound + """ + def solve(self, node: Optional[Node]) -> Optional[Node]: + clone_dict = {} + stack = [node] + visited = set() + while stack: + curr_node = stack.pop() + visited.add(curr_node.val) + if curr_node.val not in clone_dict: + clone_dict[curr_node.val] = Node(val=curr_node.val) + + for neighbor in curr_node.neighbors: + if neighbor.val not in clone_dict: + clone_dict[neighbor.val] = Node(val=neighbor.val) + + if neighbor.val not in visited: + clone_dict[curr_node.val].neighbors.append(clone_dict[neighbor.val]) + clone_dict[neighbor.val].neighbors.append(clone_dict[curr_node.val]) + stack.append(neighbor) + + return clone_dict[node.val] + + +class _LeetCodeTestCases(TestCase): + def test_1(self): + node_dict = {1: Node(1), 2: Node(2), 3: Node(3), 4: Node(4)} + node_dict[1].neighbors = [node_dict[2], node_dict[4]] + node_dict[2].neighbors = [node_dict[1], node_dict[3]] + node_dict[3].neighbors = [node_dict[2], node_dict[4]] + node_dict[4].neighbors = [node_dict[1], node_dict[3]] + + Solution.cloneGraph(Solution(), node_dict[1]) + self.assertTrue("RUN") + + +if __name__ == '__main__': + main() diff --git a/longest-common-subsequence/EGON.py b/longest-common-subsequence/EGON.py new file mode 100644 index 00000000..250a7eaf --- /dev/null +++ b/longest-common-subsequence/EGON.py @@ -0,0 +1,44 @@ +from unittest import TestCase, main + + +class Solution: + def longestCommonSubsequence(self, text1: str, text2: str) -> int: + return self.solve_dp(text1, text2) + + """ + Runtime: 441 ms (Beats 85.60%) + Time Complexity: O(m * n) + - text1, text2의 길이를 각각 m, n이라 하면 이중 for문 조회에 O(m * n) + - i. dp 배열 값 갱신에서 기존값 +1에 O(1) + - ii. dp 배열 값 갱신에서 2개 항에 대한 max 연산에 O(2), upper bound + > O(m * n) * O(2) ~= O(m * n) + + Memory: 41.81 (Beats 55.93%) + Space Complexity: O(m * n) + > row의 길이가 n이가 col의 길이가 m인 2차원 배열 dp 사용에 O(m * n) + """ + def solve_dp(self, text1: str, text2: str) -> int: + dp = [[0] * (len(text2) + 1) for _ in range(len(text1) + 1)] + for idx1 in range(len(text1)): + for idx2 in range(len(text2)): + if text1[idx1] == text2[idx2]: + dp[idx1 + 1][idx2 + 1] = dp[idx1][idx2] + 1 + else: + dp[idx1 + 1][idx2 + 1] = max(dp[idx1 + 1][idx2], dp[idx1][idx2 + 1]) + + return dp[-1][-1] + + +class _LeetCodeTestCases(TestCase): + def test_1(self): + text1 = "abcde" + text2 = "ace" + output = 3 + self.assertEqual( + Solution().longestCommonSubsequence(text1, text2), + output + ) + + +if __name__ == '__main__': + main() diff --git a/longest-repeating-character-replacement/EGON.py b/longest-repeating-character-replacement/EGON.py new file mode 100644 index 00000000..6c0257d8 --- /dev/null +++ b/longest-repeating-character-replacement/EGON.py @@ -0,0 +1,48 @@ +from unittest import TestCase, main +from collections import defaultdict + + +class Solution: + def characterReplacement(self, s: str, k: int) -> int: + return self.solve_sliding_window(s, k) + + """ + Runtime: 63 ms (Beats 98.40%) + Time Complexity: O(n) + - s에 대해 조회하는데 O(n) + - dict인 counter를 사용해서 특정 문자의 빈도수 체크, 분기처리, most_common_char 갱신에 O(1) + - 현재 최장 길이 계산에 int 계산만 사용하므로 O(1) + > O(n) * O(1) ~= O(n) + + Memory: 16.54 (Beats 76.70%) + Space Complexity: O(n), upper bound + - left, right 같은 int 인덱스 변수 할당에 O(1) + - most_common_char같은 str 변수 할당에 O(1) + - dict인 counter의 최대 크기는 s의 모든 문자가 다르고 k의 크기가 len(s)인 경우이므로 O(n), upper bound + > O(n), upper bound + """ + def solve_sliding_window(self, s: str, k: int) -> int: + left = right = 0 + counter, most_common_char = defaultdict(int), "" + for right in range(len(s)): + counter[s[right]] += 1 + if counter[most_common_char] < counter[s[right]]: + most_common_char = s[right] + + if (right - left + 1) - counter[most_common_char] > k: + counter[s[left]] -= 1 + left += 1 + + return right - left + 1 + + +class _LeetCodeTestCases(TestCase): + def test_1(self): + s = "AABBCC" + k = 2 + output = 4 + self.assertEqual(Solution.characterReplacement(Solution(), s, k), output) + + +if __name__ == '__main__': + main() diff --git a/merge-two-sorted-lists/EGON.py b/merge-two-sorted-lists/EGON.py new file mode 100644 index 00000000..c1355dd2 --- /dev/null +++ b/merge-two-sorted-lists/EGON.py @@ -0,0 +1,79 @@ +from typing import Optional +from unittest import TestCase, main + + +class ListNode: + def __init__(self, val=0, next=None): + self.val = val + self.next = next + + +class Solution: + def mergeTwoLists(self, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]: + return self.solve(list1, list2) + + """ + Runtime: 36 ms (Beats 71.91%) + Time Complexity: O(m + n) + > list1의 길이를 m, list2의 길이를 n이라 할 때, list1과 list2 모두 끝까지 조회하므로 O(n + m) + + Memory: 16.62 (Beats 37.59%) + Space Complexity: O(m + n) + > result의 길이는 list1의 길이와 list2의 길이의 합과 같으므로 O(m + n) + """ + def solve(self, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]: + result = ListNode() + node = result + while list1 or list2: + if list1 and list2: + if list1.val < list2.val: + node.next = list1 + node = node.next + list1 = list1.next + else: + node.next = list2 + node = node.next + list2 = list2.next + + elif list1 and not list2: + node.next = list1 + node = node.next + list1 = list1.next + + elif not list1 and list2: + node.next = list2 + node = node.next + list2 = list2.next + + else: + break + + return result.next + + +class _LeetCodeTestCases(TestCase): + def test_1(self): + list1 = ListNode( + val=1, + next=ListNode( + val=2, + next=ListNode( + val=4 + ) + ) + ) + list2 = ListNode( + val=1, + next=ListNode( + val=3, + next=ListNode( + val=4 + ) + ) + ) + output = [1,1,2,3,4,4] + self.assertEqual(Solution.mergeTwoLists(Solution(), list1, list2), output) + + +if __name__ == '__main__': + main() diff --git a/sum-of-two-integers/EGON.py b/sum-of-two-integers/EGON.py new file mode 100644 index 00000000..23074021 --- /dev/null +++ b/sum-of-two-integers/EGON.py @@ -0,0 +1,41 @@ +from unittest import TestCase, main + + +class Solution: + def getSum(self, a: int, b: int) -> int: + return self.solve_bit(a, b) + + """ + Runtime: 26 ms (Beats 94.94%) + Time Complexity: O(log max(a, b)) ~= O(1) + - a와 b를 and 연산 후 왼쪽으로 shift한 값을 b에 할당하는데 모두 비트연산이므로 O(1) + - b의 값이 왼쪽 shift에 의해 감소하므로 log(b) + - 단 b의 값은 a & b에 의해 결정되므로 log max(a, b) + - 전제 조건에서 a, b는 모두 절대값 1000 이하의 값이므로, 최대 10회의 shift 연산만 발생하므로, O(10) ~= O(1) + > O(log max(a, b)) < O(10) ~= O(1) + + Memory: 16.52 (Beats 18.98%) + Space Complexity: O(1) + > input에 무관하게 정수형 변수들만 사용하므로 O(1) + """ + def solve_bit(self, a: int, b: int) -> int: + COMPLEMENT_MASK = 0xFFF + + while b != 0: + carry = a & b + a = (a ^ b) & COMPLEMENT_MASK + b = (carry << 1) & COMPLEMENT_MASK + + return a if a <= 0x7FF else ~(a ^ COMPLEMENT_MASK) + + +class _LeetCodeTestCases(TestCase): + def test_1(self): + a = -1 + b = 1 + output = 0 + self.assertEqual(Solution().getSum(a, b), output) + + +if __name__ == '__main__': + main()