Skip to content

[seungriyou] Week 01 Solutions #1134

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Apr 4, 2025
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
37 changes: 37 additions & 0 deletions contains-duplicate/seungriyou.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# https://leetcode.com/problems/contains-duplicate/

from typing import List

class Solution:
def containsDuplicate1(self, nums: List[int]) -> bool:
"""
[Complexity]
- TC: O(n) (set(nums) 시 원소를 복사하는 데에 O(n), len()은 O(1))
- SC: O(n)

[Approach]
set(hash map)을 이용하여 중복을 제거한 결과의 길이를 원본 리스트의 길이와 비교하면 된다.
"""

return len(nums) != len(set(nums))


def containsDuplicate(self, nums: List[int]) -> bool:
"""
[Complexity]
- TC: O(n) (iteration)
- SC: O(n)

[Approach]
nums를 순회하면서 set(hash map)에 등장했던 원소를 add 하고, 동일한 원소가 발견되면 True를 반환한다.
nums 전체를 순회하면서 동일한 원소가 없었다면 False를 반환한다.
"""

seen = set()

for num in nums: # -- O(n)
if num in seen: # -- O(1)
return True
seen.add(num)

return False
29 changes: 29 additions & 0 deletions house-robber/seungriyou.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# https://leetcode.com/problems/house-robber/

from typing import List

class Solution:
def rob(self, nums: List[int]) -> int:
"""
[Complexity]
- TC: O(n)
- SC: O(1) (space-optimized DP)

[Approach]
인접한 두 집을 모두 털면 안 되므로 다음과 같이 dp table을 구상할 수 있다.
dp[i] = (순차적으로) i-th house를 털 때 얻을 수 있는 max amount of money
= max(이전 집을 털었을 때, 이전 집을 털지 않았을 때)
= max(지금 집을 털 수 없을 때, 지금 집을 털 수 있을 때)
= max(dp[i - 1], dp[i - 2] + nums[i])
이때, dp[i] 값을 채우기 위해 dp[i - 1]과 dp[i - 2] 값만 필요하므로,
O(n) space(= list)가 아닌 O(1) space(= variables)로 optimize 할 수 있다.
prev1 = dp[i - 1]
prev2 = dp[i - 2]
"""

prev1 = prev2 = 0

for num in nums:
prev1, prev2 = max(prev1, prev2 + num), prev1 # -- multiple assignment

return prev1
69 changes: 69 additions & 0 deletions longest-consecutive-sequence/seungriyou.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# https://leetcode.com/problems/longest-consecutive-sequence/

from typing import List

class Solution:
def longestConsecutive1(self, nums: List[int]) -> int:
"""
[Complexity]
- TC: O(n)
- SC: O(n) (unique_nums)

[Approach]
O(n) time에 돌아가야 하므로 O(nlogn)인 sorting은 사용할 수 없다.
그리고 integer에서 consecutive 라는 것은 1 차이라는 것이다.
따라서 hash map에서 현재 보고 있는 num 값에 대해 left(= num - 1)와 right(= num + 1)를 O(1) time에 찾아보는 방식으로 접근해야 한다.
이때, left와 right를 양옆으로 확장시켜가면서 max_length = max(max_length, right - left - 1)로 업데이트 한다.
"""

max_length = 0
unique_nums = set(nums)

for num in nums:
# num의 consecutive integer인 left, right 구하기
left, right = num - 1, num + 1

# left, right 양옆으로 확장하며 unique_nums에서 지워나가기
while left in unique_nums:
unique_nums.remove(left)
left -= 1
while right in unique_nums:
unique_nums.remove(right)
right += 1

# 현재 보고 있는 num이 속하는 consecutive sequence의 length는 (right - left - 1)
max_length = max(max_length, right - left - 1)

# unique_nums가 비었으면, 더이상 확인할 필요가 없음
if not unique_nums:
break

return max_length

def longestConsecutive(self, nums: List[int]) -> int:
"""
[Complexity]
- TC: O(n)
- SC: O(n) (unique_nums)

[Approach]
첫 번째 풀이와 비슷하나, unique_nums를 순회하다가 현재 보고 있는 num이 자신이 속한 consecutive sequence의 가장 left 값일 때만
오른쪽으로 확장한다는 점에서 약간 다른 풀이이다.
이때, 오른쪽으로 확장 후 max_length = max(max_length, right - num)로 업데이트 한다.
"""

max_length = 0
unique_nums = set(nums)

for num in unique_nums:
# 현재 보고 있는 num이, 자신이 속한 consecutive sequence의 가장 left 값이라면,
if num - 1 not in unique_nums:
# 오른쪽으로 확장하기
right = num + 1
while right in unique_nums:
right += 1

# 현재 보고 있는 num이 첫 번째 값인 consecutive sequence의 length는 (right - num)
max_length = max(max_length, right - num)

return max_length
37 changes: 37 additions & 0 deletions top-k-frequent-elements/seungriyou.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# https://leetcode.com/problems/top-k-frequent-elements/

from typing import List

class Solution:
def topKFrequent(self, nums: List[int], k: int) -> List[int]:
"""
[Complexity]
- TC: O(n + klogn)
- SC: O(n)

[Approach]
1. nums를 순회하면서 원소별 등장 횟수를 카운트한다.
2. min heap에 (-등장 횟수, 원소) 형태의 tuple을 저장한다.
3. k번 pop 하면, k most frequent elements를 얻을 수 있다.
"""
import heapq
from collections import Counter

# 0. early stop
if len(nums) == k:
return nums

# 1. 등장 횟수 카운트
cnt = Counter(nums) # -- O(n)

# 2. min heap에 저장
q = [(-c, num) for num, c in cnt.items()] # -- O(n)
heapq.heapify(q) # -- O(n)

# 3. k번 pop
res = []
for _ in range(k): # -- O(k)
_, num = heapq.heappop(q) # -- O(logn)
res.append(num)

return res
26 changes: 26 additions & 0 deletions two-sum/seungriyou.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# https://leetcode.com/problems/two-sum/

from typing import List

class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
"""
[Complexity]
- TC: O(n)
- SC: O(n)

[Approach]
항상 하나의 정답이 존재하기 때문에, 복잡도를 O(n^2)보다 줄이기 위해서는 hash map에 (target - num) 값을 저장함으로써
현재 보고있는 값과 더했을 때 target이 되는 값을 찾는 과정에 드는 복잡도를 O(1)으로 줄일 수 있다.
1. nums를 순회하며, hash map의 key에 현재 값이 존재하는지(= 현재 값과 더했을 때 target이 되는 값이 있는지) 확인한다.
2. 존재한다면, 현재 값의 index와 hash map의 value에 기록되어 있는 index를 반환한다.
3. 존재하지 않는다면, hash map에 {(target - num): index}를 추가한다.
"""

remains = dict()

for i, num in enumerate(nums):
if num in remains:
return [remains[num], i]

remains[target - num] = i