From e488dfb371972b97d18d3bb970f39488950aad69 Mon Sep 17 00:00:00 2001 From: thispath98 Date: Tue, 31 Dec 2024 13:11:47 +0900 Subject: [PATCH 1/7] feat: Add Merge Two Sorted Lists solutions --- merge-two-sorted-lists/thispath98.py | 44 ++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 merge-two-sorted-lists/thispath98.py diff --git a/merge-two-sorted-lists/thispath98.py b/merge-two-sorted-lists/thispath98.py new file mode 100644 index 000000000..9b91d0425 --- /dev/null +++ b/merge-two-sorted-lists/thispath98.py @@ -0,0 +1,44 @@ +# Definition for singly-linked list. +# 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]: + """ + Intuition: + 두 리스트의 원소를 각각 비교하면서 한번씩 스캔한다. + 결과적으로 한번씩만 스캔하면 정렬할 수 있다. + + Time Complexity: + O(N): + 두개의 리스트를 1번 순회하며 답을 찾으므로, + O(N)의 시간복잡도가 소요된다. + + Space Complexity: + O(N): + sorted_list에 정렬된 배열을 저장하므로, + O(N)의 공간복잡도가 소요된다. + """ + sorted_list = [] + while list1 is not None and list2 is not None: + if list1.val < list2.val: + sorted_list.append(list1.val) + list1 = list1.next + else: + sorted_list.append(list2.val) + list2 = list2.next + + while list1 is not None: + sorted_list.append(list1.val) + list1 = list1.next + while list2 is not None: + sorted_list.append(list2.val) + list2 = list2.next + + sorted_node = None + while sorted_list: + val = sorted_list.pop() + sorted_node = ListNode(val, sorted_node) + + return sorted_node From 5b779824c0341ff927b128ef246ae504bfabfee5 Mon Sep 17 00:00:00 2001 From: thispath98 Date: Tue, 31 Dec 2024 13:40:37 +0900 Subject: [PATCH 2/7] feat: Add Merge Two Sorted Lists another solutions --- merge-two-sorted-lists/thispath98.py | 44 +++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/merge-two-sorted-lists/thispath98.py b/merge-two-sorted-lists/thispath98.py index 9b91d0425..9848d6758 100644 --- a/merge-two-sorted-lists/thispath98.py +++ b/merge-two-sorted-lists/thispath98.py @@ -4,7 +4,7 @@ # self.val = val # self.next = next class Solution: - def mergeTwoLists(self, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]: + def mergeTwoListsList(self, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]: """ Intuition: 두 리스트의 원소를 각각 비교하면서 한번씩 스캔한다. @@ -42,3 +42,45 @@ def mergeTwoLists(self, list1: Optional[ListNode], list2: Optional[ListNode]) -> sorted_node = ListNode(val, sorted_node) return sorted_node + + def mergeTwoListsNode(self, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]: + """ + Intuition: + 파이썬 리스트를 사용하지 않고 + 주어진 ListNode로부터 바로 시작한다. + + Time Complexity: + O(N): + 두개의 리스트를 1번 순회하며 답을 찾으므로, + O(N)의 시간복잡도가 소요된다. + + Space Complexity: + O(1): + ListNode를 바로 사용하므로 + 상수 만큼의 O(1)의 공간복잡도가 소요된다. + + Key takeaway: + 링크드 리스트를 오랜만에 접하니 잘 풀지 못했던 것 같다. + 전통적인 자료구조를 OOP 관점으로 고민해보자. + """ + sorted_node = ListNode() + current_node = sorted_node + + while True: + if list1 is None: + current_node.next = list2 + break + elif list2 is None: + current_node.next = list1 + break + + if list1.val < list2.val: + current_node.next = ListNode(list1.val) + current_node = current_node.next + list1 = list1.next + else: + current_node.next = ListNode(list2.val) + current_node = current_node.next + list2 = list2.next + + return sorted_node.next From 4b57613d7f2adb7e3537f683637d5083ea600479 Mon Sep 17 00:00:00 2001 From: thispath98 Date: Tue, 31 Dec 2024 14:03:42 +0900 Subject: [PATCH 3/7] feat: Add Missing Number solutions --- missing-number/thispath98.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 missing-number/thispath98.py diff --git a/missing-number/thispath98.py b/missing-number/thispath98.py new file mode 100644 index 000000000..2719095ee --- /dev/null +++ b/missing-number/thispath98.py @@ -0,0 +1,23 @@ +class Solution: + def missingNumber(self, nums: List[int]) -> int: + """ + Intuition: + 주어진 리스트의 개수를 얻어 범위를 구한다. + 이후 세트를 이용해서 범위 내의 정수가 + 세트 안에 없으면 그 수를 리턴한다. + + Time Complexity: + O(N): + 세트(해시)는 접근하는 데에 상수의 시간이 걸리므로 + 최대 N + 1번의 접근을 하므로 + O(N)의 시간복잡도가 소요된다. + + Space Complexity: + O(N): + 리스트를 해시로 변환하여 저장하고 있으므로 + O(N)의 공간복잡도가 소요된다. + """ + num_set = set(nums) + for i in range(len(nums) + 1): + if i not in num_set: + return i From bf45649a840157c47ffd1fc310d5f916757a9391 Mon Sep 17 00:00:00 2001 From: thispath98 Date: Sat, 4 Jan 2025 10:32:37 +0900 Subject: [PATCH 4/7] feat: Add Word Search solutions --- word-search/thispath98.py | 48 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 word-search/thispath98.py diff --git a/word-search/thispath98.py b/word-search/thispath98.py new file mode 100644 index 000000000..80c791ab3 --- /dev/null +++ b/word-search/thispath98.py @@ -0,0 +1,48 @@ +class Solution: + def exist(self, board: List[List[str]], word: str) -> bool: + """ + Intuition: + 보드를 돌면서 dfs를 수행한다. + dfs는 상하좌우를 돌면서 word의 index와 + board의 word가 동일한지 확인한다. + + Time Complexity: + O(M x N + w.length^4): + M x N 크기의 배열을 돌면서, + 각 칸마다 상하좌우 4번씩 확인한다. + 최대 word length번만큼 반복한다. + + Space Complexity: + O(M x N + w.length): + M x N 크기의 visited 배열을 초기화하고, + dfs의 호출 스택은 word length만큼 반복한다. + """ + visited = [[False for _ in board[0]] for _ in board] + + def dfs(y, x, index): + if index == len(word): + return True + if not (0 <= y < len(board) and 0 <= x < len(board[0])): + return False + if visited[y][x]: + return False + if word[index] != board[y][x]: + return False + + visited[y][x] = True + for dy, dx in [[-1, 0], [1, 0], [0, -1], [0, 1]]: + ny = y + dy + nx = x + dx + + if dfs(ny, nx, index + 1): + return True + + visited[y][x] = False + return False + + for i in range(len(board)): + for j in range(len(board[0])): + if dfs(i, j, 0): + return True + + return False From a338957c79421aaec61493bb10749e1e8f04a66b Mon Sep 17 00:00:00 2001 From: thispath98 Date: Sat, 4 Jan 2025 10:32:54 +0900 Subject: [PATCH 5/7] feat: Add Palindromic Substrings solutions --- palindromic-substrings/thispath98.py | 33 ++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 palindromic-substrings/thispath98.py diff --git a/palindromic-substrings/thispath98.py b/palindromic-substrings/thispath98.py new file mode 100644 index 000000000..bbd83b4f9 --- /dev/null +++ b/palindromic-substrings/thispath98.py @@ -0,0 +1,33 @@ +class Solution: + def countSubstrings(self, s: str) -> int: + """ + Intuition: + 2중 루프를 돌면서 각 substring에 대해 + palindrome인지 아닌지 확인한다. + 한번 palindrome인지 확인했으면, set에 추가하여 + 중복 확인을 한다. + + Time Complexity: + O(N^2 x s.length): + 2중 루프는 N^2만큼 소요되고, + 각 루프에 palindrome을 체크하는 것은 + s.length만큼 소요된다. + + Space Complexity: + O(N^2): + palindrome이 모두 중복되지 않을 경우 set에 + s의 substring 개수만큼 저장한다. + 이는 대략 N^2이다. + """ + def is_palindrome(s): + return s == s[::-1] + + palindrome_set = set() + answer = 0 + for i in range(1, len(s) + 1): + for j in range(0, len(s) - i + 1): + substr = s[j: j + i] + if substr in palindrome_set or is_palindrome(substr): + palindrome_set.add(substr) + answer += 1 + return answer From 3e8a753c3af93ec6d4593ba62af0415cabf7609a Mon Sep 17 00:00:00 2001 From: thispath98 Date: Sat, 4 Jan 2025 10:33:10 +0900 Subject: [PATCH 6/7] feat: Add Coin Change solutions --- coin-change/thispath98.py | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 coin-change/thispath98.py diff --git a/coin-change/thispath98.py b/coin-change/thispath98.py new file mode 100644 index 000000000..a3edc7571 --- /dev/null +++ b/coin-change/thispath98.py @@ -0,0 +1,39 @@ +class Solution: + def coinChange(self, coins: List[int], amount: int) -> int: + """ + Intuition: + dp 배열에 이전 금액에 대한 최소 개수를 저장해두고 + 갱신하는 방식으로 작동한다. + + for 루프를 돌면서 현재 가격에서 coin만큼의 가격을 + 뺐을 때 거슬러줄 수 있다면, 그 값에서 1개를 더해준 + 개수를 prev_coins에 저장한다. + + 이후 prev_coins가 존재하면 현재 인덱스에서 거슬러줄 수 있는 + 동전의 최소 개수를 갱신한다. + + Time Complexity: + O(amount x coins.length): + amount 만큼 루프를 순회하는데 각 루프마다 + coins.length 만큼 prev_coins 배열을 만든다. + + Space Complexity: + O(amount): + amount만큼의 크기를 가지는 dp 배열을 저장한다. + """ + dp = [0 for _ in range(amount + 1)] + + for coin in coins: + if coin <= amount: + dp[coin] = 1 + + for i in range(1, amount + 1): + if dp[i]: + continue + + prev_coins = [dp[i - coin] + 1 for coin in coins if i >= coin and dp[i - coin] > 0] + if prev_coins: + dp[i] = min(prev_coins) + + answer = -1 if amount > 0 and dp[amount] == 0 else dp[amount] + return answer From a30cffa080e3a8e2eb02ba9892e61d4eb367b281 Mon Sep 17 00:00:00 2001 From: thispath98 Date: Sat, 4 Jan 2025 11:55:28 +0900 Subject: [PATCH 7/7] feat: Add additional soultions for Palindromic Substrings --- palindromic-substrings/thispath98.py | 97 ++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/palindromic-substrings/thispath98.py b/palindromic-substrings/thispath98.py index bbd83b4f9..9e83ef41f 100644 --- a/palindromic-substrings/thispath98.py +++ b/palindromic-substrings/thispath98.py @@ -31,3 +31,100 @@ def is_palindrome(s): palindrome_set.add(substr) answer += 1 return answer + + +class SolutionDPSet: + def countSubstrings(self, s: str) -> int: + """ + Intuition: + 위 solution에서 중복을 제거할 수 있는 방법은, + start : end 길이를 갖는 substring에서 + s[start] == s[end]이고, start + 1 : end - 1의 + substring이 palindrome이라면, 이 substring은 + palindrome이라고 볼 수 있다. + + Time Complexity: + O(N^2): + DP로 인해 palindrome을 찾는 함수가 대략 + 상수의 시간복잡도가 걸린다고 볼 수 있다. + 따라서 substring을 만드는 이중 루프에서의 + 시간복잡도가 걸릴 수 있다고 보면 된다. + + Space Complexity: + O(N^2): + dp set에 index set을 저장하는데, 최악의 경우 + index set은 N^2개만큼 저장될 수 있다. + + Key takeaway: + dp를 이용해서 푸는 방식에 대해 익숙해져야겠다. + + 의문점은 leetcode에서 bruteforce보다 시간이 더 소요되었다는 것이다. + 아마 list 크기를 초과할 경우에 append를 할 경우, + 리스트 크기를 2배만큼 늘리는 list doubling 방식이 + set에도 적용이 되어 느려진 것으로 보인다. + """ + dp = set() + + + def is_palindrome(start, end): + while start < end: + if s[start] != s[end]: + return False + if (start, end) in dp: + return True + start += 1 + end -= 1 + + return True + + + answer = 0 + for length in range(1, len(s) + 1): + for start in range(0, len(s) - length + 1): + end = start + length - 1 + if (start, end) in dp or is_palindrome(start, end): + dp.add((start, end)) + answer += 1 + return answer + + +class SolutionDPList: + def countSubstrings(self, s: str) -> int: + """ + Intuition: + DP solution에서 set로 저장하지 않고, + 이중 리스트로 저장하는 것으로 수정했다. + length = 2인 경우에는 start와 end만 동일하면 + palindrome으로 판단할 수 있어 조건을 추가했다. + + Time Complexity: + O(N^2): + DP로 인해 palindrome을 찾는 함수가 대략 + 상수의 시간복잡도가 걸린다고 볼 수 있다. + 따라서 substring을 만드는 이중 루프에서의 + 시간복잡도가 걸릴 수 있다고 보면 된다. + + Space Complexity: + O(N^2): + dp 리스트에 substring 이중 리스트를 저장하므로 + N^2개만큼 저장될 수 있다. + + Key takeaway: + 이 방법이 가장 빠르게 동작했다. + """ + dp = [[False for _ in s] for _ in s] + answer = 0 + + for i in range(len(s)): + dp[i][i] = True + answer += 1 + + for length in range(2, len(s) + 1): + for start in range(len(s) - length + 1): + end = start + length - 1 + if s[start] == s[end]: + if length == 2 or dp[start + 1][end - 1]: + dp[start][end] = True + answer += 1 + + return answer