diff --git a/contains-duplicate/kangdaia.py b/contains-duplicate/kangdaia.py new file mode 100644 index 000000000..6acbf4adb --- /dev/null +++ b/contains-duplicate/kangdaia.py @@ -0,0 +1,25 @@ +class Solution: + def containsDuplicate(self, nums: list[int]) -> bool: + """ + 리스트에 숫자가 두번 이상 등장하는 경우가 있으면 True, + 모든 숫자가 한 번씩만 등장하면 (unique) False를 반환하는 함수 + + 방법: + 1. 처음부터 끝까지 리스트를 순회하면서 동일한 숫자가 있는지 확인하는 방법 (with dict); o(n) 시간복잡도 + 2. 리스트를 set으로 변환하여 길이를 비교하는 방법; o(n) 시간복잡도 -> PICK! + 3. 리스트를 정렬한 후 인접한 숫자가 동일한지 확인하는 방법; o(nlogn) 시간복잡도 + + * 길이가 1일 경우, 중복이 있을 수 없으므로 False를 반환한다. + + 방법(2)가 가장 간단하고 효율적이지만, 메모리 사용량은 길이가 n인 리스트를 set으로 변환할 때 O(n) 만큼 필요하다. + 방법(3)은 추가적인 메모리를 사용하지 않지만, 정렬할 때 O(nlogn) 시간복잡도가 든다. + + Args: + nums (List[int]): 동일한 숫자를 포함한 정수 배열 + + Returns: + bool: 중복이 없으면 False, 있으면 True + """ + if len(nums) <= 1: + return False + return len(nums) != len(set(nums)) diff --git a/house-robber/kangdaia.py b/house-robber/kangdaia.py new file mode 100644 index 000000000..025f13907 --- /dev/null +++ b/house-robber/kangdaia.py @@ -0,0 +1,29 @@ +class Solution: + def rob(self, nums: list[int]) -> int: + """ + 연속된 집을 훔치지 않고 최대한 많은 돈을 훔치는 함수 + + 방법 + 1. DP 방식으로, 각 집을 훔칠 때의 최대 돈의 양을 업데이트 하는 방법. + dp[0], dp[1]를 초기화 하고, dp[2]는 dp[0]+nums[2]로 구성 + 이후 dp[i]는 dp[i-2]와 dp[i-3] 중 큰 값에 nums[i]를 더한 값으로 구성 + max(dp)로 최대값 찾기 + 2. 1번 방법에서 점화식을 좀 더 간단하게, dp[i]는 dp[i-1]과 dp[i-2]+nums[i] 중 큰 값으로 구성하는 방법. + dp[i-1]은 i번째 집을 훔치지 않았을 때의 최대값, dp[i-2]+nums[i]는 i번째 집을 훔쳤을 때의 최대값이므로, 둘 중 큰 값을 dp[i]로 업데이트 하는 방식. + dp[-1]이 최대값이므로, max(dp) 대신 dp[-1]로 최대값 찾기 + 3. 2번 방법에서 dp 리스트 대신, 두 개의 변수로 이전 두 집을 훔쳤을 때의 최대값을 업데이트 하는 방법. + + Args: + nums (list[int]): 각 집에 있는 돈의 양이 담긴 리스트 + + Returns: + int: 최대로 훔칠 수 있는 돈의 양 + """ + if len(nums) <= 2: + return max(nums) + prev1, prev2 = 0, 0 + for num in nums: + temp = prev1 + prev1 = max(prev1, prev2 + num) + prev2 = temp + return prev1 diff --git a/longest-consecutive-sequence/kangdaia.py b/longest-consecutive-sequence/kangdaia.py new file mode 100644 index 000000000..8ceeddab4 --- /dev/null +++ b/longest-consecutive-sequence/kangdaia.py @@ -0,0 +1,32 @@ +class Solution: + def longestConsecutive(self, nums: list[int]) -> int: + """ + 가장 길게 연속된 숫자들의 길이를 반환하는 함수 + ** 조건: O(n) 시간복잡도로 풀이해야 한다. + + 방법 + 0. set 자료구조를 사용해 중복 제거 + 1. 정렬한 후 순회하면 연속된 숫자가 끊기는 지점을 찾아 max 길이를 업데이트 하는 방법. + => O(nlogn) => 시간복잡도 조건을 만족하지 못한다. + 2. DP 방식으로 max(list) 만큼 리스트를 만들어, 각 숫자가 존재하는지 체크하면서 연속된 숫자들의 길이를 업데이트 하는 방법. + => 음수 숫자가 존재할 수 있어, 해당 방법은 사용할 수 없다. + 3. 목록을 순회하면서, 각 숫자가 연속된 숫자의 시작점인지 확인한 후, 연속된 숫자들의 길이를 파악해, max 길이를 업데이트 하는 방법. + => O(n) + 4. union-find 자료구조를 사용해, 각 숫자들을 연결한 후, 가장 긴 연결된 숫자들의 길이를 반환하는 방법. + => O(n), 구현 x + + Args: + nums (list[int]): 정렬되지 않은 중복 포함 정수 배열 + + Returns: + int: 가장 길게 연속된 숫자들의 길이 + """ + u_nums = set(nums) + longest = 0 + for u_num in u_nums: + if u_num - 1 not in u_nums: + local = 1 + while u_num + local in u_nums: + local += 1 + longest = max(local, longest) + return longest diff --git a/top-k-frequent-elements/kangdaia.py b/top-k-frequent-elements/kangdaia.py new file mode 100644 index 000000000..c409503d6 --- /dev/null +++ b/top-k-frequent-elements/kangdaia.py @@ -0,0 +1,27 @@ +import heapq +from collections import Counter + +class Solution: + def topKFrequent(self, nums: list[int], k: int) -> list[int]: + """반복되는 횟수가 높은 순으로 Top k인 숫자들을 찾는 함수 + + 방법 + 1. 전체 목록을 순회하면서, 각 숫자가 몇 번 나오는지 세어, dict map 만들기. + 이후 value 값 기준으로 정렬해, 상위 k개를 반환하는 방법 => O(n) + O(n log n) => O(n log n) + 2. 1번의 방법에서 dict map을 만드는 걸, python 내장 모듈인 Counter로 대체하기. + Counter가 약간 더 빠름; C implementation이기 때문. + 3. 1번 방법의 sorted 대신, heapq 모듈의 nlargest 함수를 이용하기. + 전체 목록을 순회하지 않고, top k에 해당하는 값만 파악하기에 효율적임. O(n) + O(n log k) => O(n log k) + 4. quick select 알고리즘. O(n) + O(n) => O(n); 해당 문제에 구현하지 못함. + + Args: + nums (list[int]): 정렬되지 않은 중복 포함 정수 배열 + k (int): Top k의 개수 + + Returns: + list[int]: Top k에 해당하는 숫자들의 리스트 + """ + if len(nums) <= 1: + return nums + count = Counter(nums) + return heapq.nlargest(k, count.keys(), key=count.get) diff --git a/two-sum/kangdaia.py b/two-sum/kangdaia.py new file mode 100644 index 000000000..b9684b56d --- /dev/null +++ b/two-sum/kangdaia.py @@ -0,0 +1,29 @@ +class Solution: + def twoSum(self, nums: list[int], target: int) -> list[int]: + """ + 리스트에서 두 숫자의 합이 target이 되는 경우가 있으면, 그 두 숫자의 인덱스를 반환하는 함수 + + 방법: + 1. 리스트를 순회하면서, 각 숫자에서 target을 만들기 위한 나머지 숫자가 있는지 모두 순회 (brute-force); o(n^2) 시간복잡도 + 2. 리스트를 순회하면서, 각 숫자에서 target을 만들기 위한 나머지 숫자가 있는지 search (binary search); o(nlogn) 시간복잡도 + 3. 리스트를 순회하면서, 각 숫자를 dict에 저장하여 (key=숫자, value=인덱스), + target을 만들기 위한 나머지 숫자를 dict에서 search; o(n) 시간복잡도 -> PICK! + + * 답이 항상 존재한다는 가정하에 문제를 풀이. + * 리스트의 길이가 2 이하인 경우, 두 숫자의 합이 target이 되는 경우가 항상 존재하므로 [0, 1]을 반환한다. + + Args: + nums (list[int]): 중복을 포함한 정수 배열 + target (int): 찾아야 하는 두 숫자의 합 + + Returns: + list[int]: 찾아낸 두 숫자의 인덱스 + """ + if len(nums) <= 2: + return [0, 1] + seen = dict() + for i, num in enumerate(nums): + remain = target - num + if remain in seen: + return [seen[remain], i] + seen[num] = i