diff --git a/contains-duplicate/jongwanra.py b/contains-duplicate/jongwanra.py new file mode 100644 index 0000000000..3ba0e593f9 --- /dev/null +++ b/contains-duplicate/jongwanra.py @@ -0,0 +1,18 @@ + +""" +[문제] +https://leetcode.com/problems/contains-duplicate/description/ + +[문제 접근 방법] +Set 자료구조를 활용하여 중복 여부를 개수로 확인한다. + +[Complexity] +N: nums 길이 +TC: O(N) +SC: O(N) +""" + +class Solution(object): + def containsDuplicate(self, nums): + return len(set(nums)) != len(nums) + diff --git a/house-robber/jongwanra.py b/house-robber/jongwanra.py new file mode 100644 index 0000000000..ed5acd161b --- /dev/null +++ b/house-robber/jongwanra.py @@ -0,0 +1,81 @@ +""" +[Problem] +https://leetcode.com/problems/house-robber/description/ +""" + +class Solution(object): + """ + [Brain Storming] + 같은 날에, 인접한 집을 털면 안된다. + 하루에 털 수 있는 최대 금액을 구하는 문제. + + 1 <= nums.length <= 100 + nums.length가 최대 100이기 때문에, + DFS로 문제를 접근해도 풀 수 있는 문제라고 생각한다. + + 각 집을 넣는 경우와 안넣는 경우 인접한 경우를 따져서 최대 금액을 구해보자. + + [Complexity] + Time: 집을 터는 경우, 안 터는 경우 2^100.. -> Time Limit Exceeded 발생 + Space: O(N) 재귀 호출 스택의 길이도 비례하여 길어짐. + """ + def rob1(self, nums): + answer = 0 + visited = [False] * len(nums) + + def dfs(depth, sum): + # nonlocal nums + nonlocal answer + nonlocal visited + + + if depth == len(nums): + answer = max(answer, sum) + print(sum, visited) + return + + # 다음 집을 포함한 경우 + if depth == 0 or not visited[depth - 1]: + visited[depth] = True + dfs(depth + 1, sum + nums[depth]) + + # 다음 집을 포함하지 않은 경우 + visited[depth] = False + dfs(depth + 1, sum) + + dfs(0, 0) + return answer + + def rob(self, nums): + """ + 다른 사람의 풀이 + DFS + Memoization 기법을 활용한다. + ref: https://www.algodale.com/problems/house-robber/ + + [Complexity] + Time: O(N) + Space: O(N) + + """ + cache = {} + def dfs(depth): + if depth in cache: + return cache[depth] + if depth >= len(nums): + cache[depth] = 0 + return cache[depth] + + cache[depth] = max(nums[depth] + dfs(depth + 2), dfs(depth + 1)) + return cache[depth] + + return dfs(0) + + +sol = Solution() +print(sol.rob([1, 2, 3, 1])) +print(sol.rob([2,7,9,3,1])) + +# Edge Case +print(sol.rob([1]) == 1) +print(sol.rob([183,219,57,193,94,233,202,154,65,240,97,234,100,249,186,66,90,238,168,128,177,235,50,81,185,165,217,207,88,80,112,78,135,62,228,247,211])) + diff --git a/longest-consecutive-sequence/jongwanra.py b/longest-consecutive-sequence/jongwanra.py new file mode 100644 index 0000000000..de83955ce3 --- /dev/null +++ b/longest-consecutive-sequence/jongwanra.py @@ -0,0 +1,140 @@ +""" +[Problem] +https://leetcode.com/problems/longest-consecutive-sequence/description/ + +[Brain Storming] +O(N)으로 연속된 길이를 찾아야 한다? +계수 정렬을 이용하면 가능하다. 하지만 nums[i] <= 10^9이기 떄문에 공간을 너무 많이 잡아먹는다. + +[Plan] +1. max num과 min num을 찾는다. +2. set을 통해 O(1)로 빠르게 num을 찾을 수 있도록 변경한다. +3. for loop를 min num ~ max num으로 순회한다 + 3-1. 연속된 길이를 비교하며 기록한다. + +-> Time Limit Exceeded 발생 +-> [0,1,2,4,8,5,6,7,9,3,55,88,77,99,999999999] + +[Plan2] +Heap 자료구조를 이용해서 구한다. +nums를 전부 heapify할 경우 O(N), +nums를 순회하면서 heappop을 할 경우 N * log(N) +""" + +import heapq + + +class Solution(object): + """ + [Complexity] + N: nums.length + M: max_num - min_num + 1 + --- + Time: O(N + M) + Space: O(N) + + nums가 [1, 9999999] 일 경우 굉장히 비효율적인 풀이. + """ + + def longestConsecutive1(self, nums): + if not nums: + return 0 + + min_num = min(nums) + max_num = max(nums) + num_set = set(nums) + + answer = 0 + + cur_num = min_num + while cur_num <= max_num: + count = 0 + while cur_num in num_set: + count += 1 + cur_num += 1 + answer = max(answer, count) + cur_num += 1 + + return answer + + """ + [Complexity] + N: nums.length + Time: O(N * log N) + Space: O(N) + """ + + def longestConsecutive2(self, nums): + if not nums: + return 0 + heapq.heapify(nums) + + answer = 1 + prev_num = heapq.heappop(nums) + count = 1 + + while nums: + cur_num = heapq.heappop(nums) + if cur_num == prev_num: + continue + if cur_num - prev_num == 1: + # 연속인 경우 + count += 1 + prev_num = cur_num + # 연속이 아닌 경우 + else: + count = 1 + prev_num = cur_num + answer = max(answer, count) + + return answer + + """ + 다른 사람의 풀이 + 참고: https://www.algodale.com/problems/longest-consecutive-sequence/ + + [Plan] + 1. nums를 Set에 저장한다. + 2. 모든 정수에 대해서 for-loop를 순회한다. + 2-1. 정수에서 1을 뺀 값이 Set에 있다면, 구간내 첫 번째 값이 될 수 없기 때문에 무시 + 2-2. 없다면, 1씩 길이를 최대길이로 늘려본다. + 2-3. 구간내 첫 번째 값이 중복으로 들어갈 수 있는 경우를 대비하여 cache 추가 + 3. 최대 구간의 길이와 비교한다. + + [Complexity] + N: nums.length + Time: O(N) + Space: O(N) + """ + + def longestConsecutive(self, nums): + answer = 0 + num_set = set(nums) + cache = set() + + for num in nums: + if num - 1 in num_set: + continue + if num in cache: + continue + + cache.add(num) + length = 1 + while num + length in num_set: + length += 1 + + answer = max(answer, length) + return answer + + +solution = Solution() +# Normal Case +print(solution.longestConsecutive([100, 4, 200, 1, 3, 2]) == 4) +print(solution.longestConsecutive([0, 3, 7, 2, 5, 8, 4, 6, 0, 1]) == 9) +print(solution.longestConsecutive([1, 0, 1, 2]) == 3) + +# Edge Case +print(solution.longestConsecutive([]) == 0) +print(solution.longestConsecutive([0, 1, 2, 4, 8, 5, 6, 7, 9, 3, 55, 88, 77, 99, 999999999]) == 10) +print(solution.longestConsecutive([9, 1, 4, 7, 3, -1, 0, 5, 8, -1, 6]) == 7) + diff --git a/top-k-frequent-elements/jongwanra.py b/top-k-frequent-elements/jongwanra.py new file mode 100644 index 0000000000..5515976d9d --- /dev/null +++ b/top-k-frequent-elements/jongwanra.py @@ -0,0 +1,55 @@ +""" +[Problem] +https://leetcode.com/problems/top-k-frequent-elements/ + +[Brain Storming] +가장 자주 사용되는 요소 k개를 반환한다. + +1<= nums.length <= 10^5 +O(n^2) time complexity일 경우 시간 초과 발생 + +[Plan] +1. nums를 순회하면서 Hash Table에 key:element value:count 저장한다. +2. nums를 순회하면서 Heap에 (count, element)를 count를 기준으로 Max Heap 형태로 저장한다. +3. Max Heap에서 k개 만큼 pop한다. + +[Complexity] +N: nums.length, K: k +heapq.heapify(heap): O(N) +heapq.heappop(heap): log(N) + +Time: O(N + (K * log(N))) +Space: O(N + K) + * num_to_count_map: O(N) + * heap: O(N) + * answer: O(K) + + +""" + +import heapq + +class Solution(object): + def topKFrequent(self, nums, k): + num_to_count_map = {} + for num in nums: + num_to_count_map[num] = num_to_count_map.get(num, 0) + 1 + + heap = [] + for num, count in num_to_count_map.items(): + heap.append((-count, num)) + heapq.heapify(heap) + + answer = [] + for _ in range(k): + negative_count, frequent_num = heapq.heappop(heap) # log(N) + answer.append(frequent_num) + return answer + +solution = Solution() + +# Normal Case +print(solution.topKFrequent([1,1,1,2,2,3], 2) == [1, 2]) +print(solution.topKFrequent([1], 1) == [1]) + + diff --git a/two-sum/jongwanra.py b/two-sum/jongwanra.py new file mode 100644 index 0000000000..ca7610b17f --- /dev/null +++ b/two-sum/jongwanra.py @@ -0,0 +1,40 @@ +""" +[Problem] +https://leetcode.com/problems/two-sum/description/ + +nums.length <= 10^4 => 10,000 * 10,000 => n^2 time complexity일 경우 1초 이상 소요 + +[Plan] +nums를 hash table로 만들자. (key: element, value: index) +이후에 nums를 돌면서 target - current_num을 뺀 값이 hash table에 존재한 경우 return하자. +그랬을 경우, O(n) 시간 복잡도를 예상한다. + +[Complexity] +N: nums length +Time: O(N) => HashTable에 nums를 만큼 반복문 돌림 +Space: O(N) => nums length 만큼 반복문을 돌면서 hash table을 생성 +""" + +class Solution(object): + def twoSum(self, nums, target): + num_to_index_map = dict() + for index in range(len(nums)): + num_to_index_map[nums[index]] = index + + for index in range(len(nums)): + num = nums[index] + diff = target - num + # target - num 값이 존재 하지 않을 경우 무시 + if not diff in num_to_index_map: + continue + # index가 자기 자신인 경우 무시 + if index == num_to_index_map[diff]: + continue + return [index, num_to_index_map[diff]] + + return [0, 0] + + +solution = Solution() +print(solution.twoSum([3, 2, 4], 6)) +