diff --git a/contains-duplicate/seungriyou.py b/contains-duplicate/seungriyou.py new file mode 100644 index 000000000..eb360d209 --- /dev/null +++ b/contains-duplicate/seungriyou.py @@ -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 diff --git a/house-robber/seungriyou.py b/house-robber/seungriyou.py new file mode 100644 index 000000000..ff0e7211c --- /dev/null +++ b/house-robber/seungriyou.py @@ -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 diff --git a/longest-consecutive-sequence/seungriyou.py b/longest-consecutive-sequence/seungriyou.py new file mode 100644 index 000000000..35b8087db --- /dev/null +++ b/longest-consecutive-sequence/seungriyou.py @@ -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 diff --git a/top-k-frequent-elements/seungriyou.py b/top-k-frequent-elements/seungriyou.py new file mode 100644 index 000000000..a5fea95aa --- /dev/null +++ b/top-k-frequent-elements/seungriyou.py @@ -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 diff --git a/two-sum/seungriyou.py b/two-sum/seungriyou.py new file mode 100644 index 000000000..0dad7fbc0 --- /dev/null +++ b/two-sum/seungriyou.py @@ -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