diff --git a/contains-duplicate/shinsj4653.py b/contains-duplicate/shinsj4653.py new file mode 100644 index 000000000..7e7f4119f --- /dev/null +++ b/contains-duplicate/shinsj4653.py @@ -0,0 +1,30 @@ +""" +# Constraints + +1 <= nums.length <= 10^5 +-10^9 <= nums[i] <= 10^9 + +# Time Complexity: O(n) + +배열 내 원소를 순회하며 각 원소별로 등장횟수를 기록 +-> 무슨 원소가 들어있는지 모르므로, defaultdict가 좋아보임 +-> 등장 횟수 값이 2 이상인 경우 배열 순회 멈추기 + +# Space Complexity: O(n) + +최대 배열 원소 개수만큼 key-value 지니는 사전 활용 +""" + +from collections import defaultdict +class Solution: + def containsDuplicate(self, nums: List[int]) -> bool: + count_dict = defaultdict(int) + + for n in nums: + if count_dict[n] + 1 >= 2 : + return True + + else : + count_dict[n] += 1 + + return False diff --git a/house-robber/shinsj4653.py b/house-robber/shinsj4653.py new file mode 100644 index 000000000..583748e04 --- /dev/null +++ b/house-robber/shinsj4653.py @@ -0,0 +1,59 @@ +""" +Inputs: 정수형 배열 nums + +Outputs: 훔치는 돈의 max값 + +Constraints: +1 <= nums.length <= 100 +0 <= nums[i] <= 400 + +Time Complexity: O(2^n) X + +dp[n] = max(dp(n), dp(n - 2) + nums[i]) +재귀 사용 +2^(100) +2^10 = 10^3 +막상 실제 값을 출력하면 말이 안되는 값.. + +Space Complexity: O(n) +dp 배열은 nums와 똑같으므로 + +# 에러 +constraint로 인한 반례 항상 생각하도록! +len이 0이거나 1일때는 따로 처리! + +# 반례 +nums = +[2,1,1,2] + +output = 3, expected = 4 + +len(nums) - 1이 0 혹은 1일때, 즉 nums길이가 1 혹은 2일 경우엔 따로 처리 + +""" + + +class Solution: + def rob(self, nums) -> int: + dp = [-1 for _ in range(len(nums))] + n = len(nums) - 1 + + if n == 0: + return nums[0] + + # 초기값 세팅 + dp[0] = nums[0] + dp[1] = max(nums[1], nums[0]) + + if n == 1: + return dp[1] + + def memo(i): + if dp[i] != -1: + return dp[i] + + dp[i] = max(memo(i - 1), nums[i] + memo(i - 2)) + return dp[i] + + return max(memo(n - 1), nums[n] + memo(n - 2)) + diff --git a/longest-consecutive-sequence/shinsj4653.py b/longest-consecutive-sequence/shinsj4653.py new file mode 100644 index 000000000..e1877d235 --- /dev/null +++ b/longest-consecutive-sequence/shinsj4653.py @@ -0,0 +1,41 @@ +""" +Inputs: +정렬되지 않은 정수 배열 nums + +Outputs: +가장 긴 연속된 부분 배열의 길이 + +Constraints: +0 <= nums.length <= 10^5 +-10^9 <= nums[i] <= 10^9 + +Time Complexity: O(n) 이어야함 +어차피 n 최대 길이가 10^5이라 O(n^2)은 불가능! + +1 2 3 4라면, +2 3 4 +3 4 -> 이 두 후보는 정답에 포함됨 + +Space Complexity: O(n) +중복 제거한 nums 인 numSet의 크기는 최대 n +""" + + +class Solution: + def longestConsecutive(self, nums: List[int]) -> int: + numSet = set(nums) + ret = 0 + + for num in numSet: + if num - 1 in numSet: + continue + + cnt = 1 + + while num + 1 in numSet: + cnt += 1 + num += 1 + + ret = max(ret, cnt) + + return ret diff --git a/top-k-frequent-elements/shinsj4653.py b/top-k-frequent-elements/shinsj4653.py new file mode 100644 index 000000000..d15c57397 --- /dev/null +++ b/top-k-frequent-elements/shinsj4653.py @@ -0,0 +1,67 @@ +""" +Constraints: + +1 <= nums.length <= 10^5 +-10^4 <= nums[i] <= 10^4 +k is in the range [1, the number of unique elements in the array]. + +nums 배열에서 원소들의 빈도수 중, 가장 빈도수가 높은 k개의 수들 반환 + +Time Complexity: O(klogn) + +내부적으로 정렬되는 O(logn)을 가지는 최소 힙 사용? + +우선 빈도수 사전에 기록 -> dict[숫자] = 빈도수 O(n) +그 다음, 사전.items() 돌면서 heap 에 k개 만큼 넣기 O(k * logn) + +k개 만큼 우선순위 큐에 넣기 (-빈도수, 숫자) -> 튜플 넣으면 첫번쨰 원소기준으로 정렬됨 + +그리고, 나머지 사전.items() 만큼 for i in range(k + 1, n) 만큼만 돌기 +-> 만약 힙의 맨 앞 값보다 작으면 넣고, 아니라면 pass + +Space Complexity: O(n) + +nums만큼 사전에 빈도수 저장 + +# 간과한 사실 +첫 k개만 우선순위 큐에 넣고, 나머지 n - k 만큼 돌때, +힙의 맨 앞 값보다 작거나 같을 때 -> 맨 앞 값을 빼면 안됨!! 그게 정답 수 중 하나일 수 있음 + + +아뿔싸..맨 앞 값만 정렬되어있는 상태라, 마지막 값 반환 시 heappop으로 해줘야할듯! +단순히 for i in range(k) 해서 맨 앞 k 개를 반환하면 정렬되지 않은 상태에서 값을 뽑아내므로 틀릴 수 있음 + +# 반례 + +[2,3,4,1,4,0,4,-1,-2,-1] + +맨 앞의 값이랑만 비교하다보니, 최대 빈도수 값은 정확히 나오는데 두 번째 이후 값이 첫 번째 값보다 작지 않아서 힙에 못 들어온다.. +그래서, 맨 앞의 값 비교 로직 없앰!! + +하지만, 힙 내 원소를 k개를 유지 못해서 정렬 시간이 log N 이 되어버림.. +-> heap의 사이즈를 k로 유지하면서 작은 빈도 수부터 제거하면, +결국 heap 안에는 가장 많이 등장한 k개의 원소만 남는다. +""" + +from collections import defaultdict +from heapq import heappush, heappop + + +class Solution: + def topKFrequent(self, nums: List[int], k: int) -> List[int]: + freq_dict = defaultdict(int) + min_heap = [] + ret = [] + + for n in nums: + freq_dict[n] += 1 + + for key, value in freq_dict.items(): + heappush(min_heap, (value, key)) + if len(min_heap) > k: + heappop(min_heap) + + for _ in range(k): + ret.append(heappop(min_heap)[1]) + + return ret diff --git a/two-sum/shinsj4653.py b/two-sum/shinsj4653.py new file mode 100644 index 000000000..cc3545f7a --- /dev/null +++ b/two-sum/shinsj4653.py @@ -0,0 +1,47 @@ +""" +# Constraints + +2 <= nums.length <= 10^4 +-10^9 <= nums[i] <= 10^9 +-10^9 <= target <= 10^9 + +같은 숫자 두번 사용 X + +# Time Complexity: O(n) + +O(n^2) 으로도 가능은 하지만, 문제에서 이거보다 더 적은 시간복잡도로 풀 수도 있다는 말이 존재 +배열 순회하며 +첫 원소는 target - n 을 한 값을 set에 넣어두기 +다음 원소부터는 해당 값이 set에 있는지 체크 +없다면 target - n 넣기 + +근데 생각해보니 원소의 "idx" 를 반환해야함 +-> set대신 dict를 써서, +-> dict[value] = idx로 구성 +-> dict[7] = 0 +-> 배열 7 오면, 현재 idx랑 기존 idx 가져오기 가능! + +# Space Complexity: O(n) + +배열 원소 개수만큼 target - n 값이 들어갈 set 크기가 결정됨 + +""" + + +class Solution: + def twoSum(self, nums: List[int], target: int) -> List[int]: + ret = [] + diffDict = dict() + + diffDict[target - nums[0]] = 0 + + for i in range(1, len(nums)): + if nums[i] in diffDict: + ret.append(diffDict[nums[i]]) + ret.append(i) + return ret + + else: + diffDict[target - nums[i]] = i + + return ret