From 9f7fc53a5d91c5f9fff974a992534d706786316e Mon Sep 17 00:00:00 2001 From: "whatsup@lemonbase.com" Date: Sat, 8 Nov 2025 11:56:47 +0900 Subject: [PATCH 1/2] =?UTF-8?q?feat:=204=EA=B0=9C=20=EB=AC=B8=EC=A0=9C=20?= =?UTF-8?q?=ED=92=80=EC=9D=B4=20=EC=B6=94=EA=B0=80=20(two-sum,=20contains-?= =?UTF-8?q?duplicate=20=EB=93=B1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- contains-duplicate/unpo88.py | 53 +++++++++++++++ longest-consecutive-sequence/unpo88.py | 89 ++++++++++++++++++++++++++ top-k-frequent-elements/unpo88.py | 45 +++++++++++++ two-sum/unpo88.py | 80 +++++++++++++++++++++++ 4 files changed, 267 insertions(+) create mode 100644 contains-duplicate/unpo88.py create mode 100644 longest-consecutive-sequence/unpo88.py create mode 100644 top-k-frequent-elements/unpo88.py create mode 100644 two-sum/unpo88.py diff --git a/contains-duplicate/unpo88.py b/contains-duplicate/unpo88.py new file mode 100644 index 0000000000..3a12593c22 --- /dev/null +++ b/contains-duplicate/unpo88.py @@ -0,0 +1,53 @@ +class Solution: + def containsDuplicate(self, nums: list[int]) -> bool: + return len(set(nums)) != len(nums) + +""" +================================================================================ +풀이 과정 +================================================================================ + +[1차 시도] Counter를 이용한 Hash Table +──────────────────────────────────────────────────────────────────────────────── +1. Hashing을 이용해서 숫자 Count를 한 것에서 개수가 2이상인 것을 뽑으면 되겠는데? + + nums_count = Counter(nums) + for count in nums_count.values(): + if count >= 2: + return True + return False + +2. 속도가 많이 느리네 더 좋은 방법은 없을까? + + +[2차 시도] Early Return with Hash Table +──────────────────────────────────────────────────────────────────────────────── +3. 해싱을 만들면서 2개 이상이면 즉각 return 하도록 구현해볼까? + + nums_count = defaultdict(int) + for num in nums: + nums_count[num] += 1 + + if nums_count[num] == 2: + return True + return False + +4. 그래도 속도가 많이 개선되지는 않았네? + + +[최종 개선] Set을 이용한 비교 +──────────────────────────────────────────────────────────────────────────────── +5. 속도를 획기적으로 개선하려면 어떻게 접근해볼 수 있을까? +6. set으로 중복을 제거한다음에 개수를 비교해볼 수 있지 않으려나? + + return len(set(nums)) != len(nums) + +7. 조금 더 속도가 빨라졌다. + + +[성능 분석] +──────────────────────────────────────────────────────────────────────────────── +8. 근데 잘 생각해보면 Big-O 시간 복잡도는 O(n) 일 것 같은데 +9. 그러면 Early Return을 해주는 방법 2가 더 빠르다고 생각했는데 왜 3번 방식이 더 빠를까? +10. set이 CPython 내부에서 C 코드로 실행되는구나 OK +""" diff --git a/longest-consecutive-sequence/unpo88.py b/longest-consecutive-sequence/unpo88.py new file mode 100644 index 0000000000..ceaf956a3e --- /dev/null +++ b/longest-consecutive-sequence/unpo88.py @@ -0,0 +1,89 @@ +class Solution: + def longestConsecutive(self, nums: list[int]) -> int: + if not nums: + return 0 + + nums_set = set(nums) + max_length = 0 + + for num in nums_set: + # 조기 종료: 남은 숫자보다 max_length가 크면 더 이상 불가능 + if max_length >= len(nums_set): + break + + # 수열의 시작점인지 확인 (이전 값이 없어야 함) + if num - 1 not in nums_set: + current_num = num + current_length = 1 + + # 연속된 다음 숫자들을 찾아가며 길이 계산 + while current_num + 1 in nums_set: + current_num += 1 + current_length += 1 + + max_length = max(max_length, current_length) + + return max_length + +""" +================================================================================ +풀이 과정 +================================================================================ + +[문제 분석] 연속 수열의 최대 길이 찾기 +──────────────────────────────────────────────────────────────────────────────── +1. 연속 수열의 길이를 반환해야하는데 +2. O(n) 시간 내에 동작하는 알고리즘으로 만들어야하네 + → 시간을 획기적으로 줄여야하니까 set을 사용하면 좋으려나? + + +[구현 전략] 기준점 기반 탐색 +──────────────────────────────────────────────────────────────────────────────── +3. 연속 수열이니까 특정 기준점으로부터 + 1인 값이 얼마나 반복되는지 최대 길이를 구해야겠다. +4. 특정 기준점이 여러 번 바뀔 수도 있는데, set으로 중복 제거를 했으니까 문제 없어보인다. +5. 기준점을 수열의 숫자가 시작하는 시점으로 잡으려면, 수열의 이전 값이 존재하지 않아야겠네 + + nums_set = set(nums) + max_length = 0 + + for num in nums_set: + if num - 1 not in nums_set: # 수열의 시작점인 경우만 + current = num + length = 1 + + while current + 1 in nums_set: + current += 1 + length += 1 + + max_length = max(max_length, length) + + return max_length + +6. 더 개선할 부분이 있나? +7. Claude에게 코드로 질문 +8. Early Return 조건을 활용하라고 답변 (배열이 없는 경우와 남은 숫자보다 max_length가 더 큰 경우) + +def longestConsecutive(self, nums: list[int]) -> int: + if not nums: + return 0 + + nums_set = set(nums) + max_length = 0 + + for num in nums_set: + # 조기 종료: 남은 숫자보다 max_length가 크면 더 이상 불가능 + if max_length >= len(nums_set): + break + + if num - 1 not in nums_set: + current_num = num + current_length = 1 + + while current_num + 1 in nums_set: + current_num += 1 + current_length += 1 + + max_length = max(max_length, current_length) + + return max_length +""" diff --git a/top-k-frequent-elements/unpo88.py b/top-k-frequent-elements/unpo88.py new file mode 100644 index 0000000000..e4ba7394f8 --- /dev/null +++ b/top-k-frequent-elements/unpo88.py @@ -0,0 +1,45 @@ +from collections import Counter + + +class Solution: + def topKFrequent(self, nums: list[int], k: int) -> list[int]: + nums_count = Counter(nums) + return [key for key, _ in nums_count.most_common(k)] + +""" +================================================================================ +풀이 과정 +================================================================================ + +[1차 시도] Counter의 most_common() 활용 +──────────────────────────────────────────────────────────────────────────────── +1. k개의 빈도가 가장 높은 요소? +2. Counter 함수를 이용해서 K개 개수를 세면 될듯? + + nums_count = Counter(nums) + return [key for key, _ in nums_count.most_common(k)] + +[성능 분석] +3. Counter.most_common() 메서드는 내부는 어떻게 구현되어있지? + + + def most_common(self, n=None): + '''List the n most common elements and their counts from the most + common to the least. If n is None, then list all element counts. + + >>> Counter('abracadabra').most_common(3) + [('a', 5), ('b', 2), ('r', 2)] + + ''' + # Emulate Bag.sortedByCount from Smalltalk + if n is None: + return sorted(self.items(), key=_itemgetter(1), reverse=True) + + # Lazy import to speedup Python startup time + import heapq + return heapq.nlargest(n, self.items(), key=_itemgetter(1)) + +4. n이 없으면 모든 요소를 빈도수 순서대로 반환 +5. Heap 기반 알고리즘을 사용하고 있네? (Min Heap) +6. _itemgetter(1)은 카운트를 기준으로 정렬하기 위한 것 +""" diff --git a/two-sum/unpo88.py b/two-sum/unpo88.py new file mode 100644 index 0000000000..40ec8f8f58 --- /dev/null +++ b/two-sum/unpo88.py @@ -0,0 +1,80 @@ +class Solution: + def twoSum(self, nums: list[int], target: int) -> list[int]: + num_to_index = {} + + for i, num in enumerate(nums): + diff = target - num + + if diff in num_to_index: + j = num_to_index[diff] + return [i, j] + + num_to_index[num] = i + +""" +================================================================================ +풀이 과정 +================================================================================ + +[1차 시도] Brute Force - O(n^2) +──────────────────────────────────────────────────────────────────────────────── +1. 단순 반복으로 그냥 문제를 풀어보자. +2. 단 같은 숫자일 경우에는 넘어가자. + + for i in range(len(nums)): + for j in range(len(nums)): + if i == j: + continue + if nums[i] + nums[j] == target: + return [i, j] + +3. O(n^2) 으로 문제를 풀었는데 +4. 시간이 오래걸리니까 다른 풀이 방법이 있는 것 같음 + + +[2차 시도] Hash Table - Two Pass +──────────────────────────────────────────────────────────────────────────────── +5. 해싱으로 문제를 풀 방법이 있으려나? +6. 넘버마다 인덱스를 저장하고 +7. target에서 현재 가진 값을 뺀 값이 nums에 있는지를 판단하면 될 것 같은데? +8. 그리고 현재 가진 값과 뺀 값의 인덱스를 그대로 반환하면 될 것 같고 +9. 근데 동일한 인덱스는 제외해야할듯 + + num_index_dict = defaultdict() + for index, num in enumerate(nums): + num_index_dict[num]=index + print(num_index_dict) + + for index, num in enumerate(nums): + if (target - num) in nums and num_index_dict[target - num] != index: + return [index, num_index_dict[target-num]] + + +[3차 시도] Hash Table - One Pass +──────────────────────────────────────────────────────────────────────────────── +10. 단 한 번의 루프에서도 동작시킬 수 있을듯? + + num_index_dict = defaultdict() + + for index, num in enumerate(nums): + if (target - num) in num_index_dict: + return [index, num_index_dict[target - num]] + + num_index_dict[num] = index + + +[최종 개선] 코드 가독성 향상 +──────────────────────────────────────────────────────────────────────────────── +11. 속도가 많이 빨라졌다. 코드 가독성만 높여보자 + + num_to_index = {} + + for i, num in enumerate(nums): + diff = target - num + + if diff in num_to_index: + j = num_to_index[diff] + return [i, j] + + num_to_index[num] = i +""" From 288366e469f308586604abfdedc24f5325943a1c Mon Sep 17 00:00:00 2001 From: "whatsup@lemonbase.com" Date: Sat, 8 Nov 2025 19:37:37 +0900 Subject: [PATCH 2/2] =?UTF-8?q?feat:=20house-robber=20=ED=92=80=EC=9D=B4?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80=20(Memoization)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- house-robber/unpo88.py | 64 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 house-robber/unpo88.py diff --git a/house-robber/unpo88.py b/house-robber/unpo88.py new file mode 100644 index 0000000000..d807700886 --- /dev/null +++ b/house-robber/unpo88.py @@ -0,0 +1,64 @@ +class Solution: + def rob(self, nums: list[int]) -> int: + hash_value = {} + + def dfs(start): + if start in hash_value: + return hash_value[start] + + if not start < len(nums): + hash_value[start] = 0 + else: + hash_value[start] = max(nums[start] + dfs(start+2), dfs(start+1)) + return hash_value[start] + + return dfs(0) + +""" +================================================================================ +풀이 과정 +================================================================================ + +1. 양 옆을 제외한 숫자들의 합을 구해야하네? +2. 첫 번째를 선택하면 → 세 번째 ~ N번째 들의 부분합을 구하는 것 +3. 두 번째를 선택하면 → 네 번째 ~ N번째 들의 부분합을 구하는 것 +4. 뭔가 재귀적으로 구할 수 있을 것 같은데..? +5. 첫 번째 선택한 것과 두 번째 선택한 것 중 어느것이 더 큰지 비교해보면 될 것 같은데? +6. 첫 번째를 선택한 것은 → nums[0] + 재귀(nums[2:]) +7. 두 번째를 선택한 것은 → 재귀(nums[1:]) +8. 그럼 재귀함수의 로직은 어떻게 되어야하지? + + +[1차 시도] 순수 재귀 - 시간 초과 +──────────────────────────────────────────────────────────────────────────────── +9. 기본적인 재귀 구조 구현 + + def dfs(start): + if start >= len(nums): + return 0 + return max(nums[start] + dfs(start + 2), dfs(start + 1)) + + return dfs(0) + +10. 시간 초과가 발생했네? + + +11. 중간 결과값들을 저장하고 있어야할 것 같은데 +12. 해싱으로 저장해놓고 있어보자 + + hash_value = {} + + def dfs(start): + if start in hash_value: + return hash_value[start] + + if not start < len(nums): + hash_value[start] = 0 + else: + hash_value[start] = max(nums[start] + dfs(start+2), dfs(start+1)) + return hash_value[start] + + return dfs(0) + +13. 정상적으로 통과되는 것 확인 완료 +"""