Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions contains-duplicate/unpo88.py
Original file line number Diff line number Diff line change
@@ -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
"""
64 changes: 64 additions & 0 deletions house-robber/unpo88.py
Original file line number Diff line number Diff line change
@@ -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. 정상적으로 통과되는 것 확인 완료
"""
89 changes: 89 additions & 0 deletions longest-consecutive-sequence/unpo88.py
Original file line number Diff line number Diff line change
@@ -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
"""
45 changes: 45 additions & 0 deletions top-k-frequent-elements/unpo88.py
Original file line number Diff line number Diff line change
@@ -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)은 카운트를 기준으로 정렬하기 위한 것
"""
80 changes: 80 additions & 0 deletions two-sum/unpo88.py
Original file line number Diff line number Diff line change
@@ -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
"""