diff --git a/3sum/wozlsla.py b/3sum/wozlsla.py new file mode 100644 index 000000000..1b9a9569e --- /dev/null +++ b/3sum/wozlsla.py @@ -0,0 +1,96 @@ +""" +# Intuition +- + +# Approach +index가 모두 다를것, +합 = 0 -> -a = b+c +중복 X -> set : dic와 다르게 가변객체 삽입 X +Two Pointer -> 정렬된 배열을 활용 + +# Complexity +- Time complexity + - Brute-force : O(N^3) + - Set : O(N^2) - Time Limit Exceeded (memory??) + - Two Pointer : O(N^2) + +- Space complexity + - Brute-force : O(N) + - Set : O(N) + - Two Pointer : O(1) +""" + +from typing import List + + +class Solution: + def threeSum(self, nums: List[int]) -> List[List[int]]: + + triplets = set() + nums.sort() # O(NlogN), return None + + for i in range(len(nums) - 2): # O(N) + low, high = i + 1, len(nums) - 1 + + while low < high: # O(N) + three_sum = nums[i] + nums[low] + nums[high] + + if three_sum < 0: + low += 1 + elif three_sum > 0: + high -= 1 + else: + triplets.add((nums[i], nums[low], nums[high])) + low, high = low + 1, high - 1 + + return list(triplets) + + +""" Set +class Solution: + def threeSum(self, nums: List[int]) -> List[List[int]]: + + triplets = set() + + for i in range(len(nums) - 2): + seen = set() + + for j in range(i + 1, len(nums)): + complement = -(nums[i] + nums[j]) + + if complement in seen: + triplets.add(tuple(sorted([nums[i], nums[j], complement]))) + + seen.add(nums[j]) + + return list(triplets) +""" + +""" Brute-force +class Solution: + def threeSum(self, nums: List[int]) -> List[List[int]]: + + n = len(nums) + result = [] + + for i in range(n-2): + for j in range(i+1, n-1): + for k in range(j+1, n): + if i != j and i != k and j != k: + + if nums[i] + nums[j] + nums[k] == 0: + li = sorted([nums[i], nums[j], nums[k]]) + + if li not in result: # O(L) + result.append(li) + + return result +""" + +nums = [-1, 0, 1, 2, -1, -4] +sol = Solution() + +print(sol.threeSum(nums)) +# [[-1,-1,2],[-1,0,1]] + +# print(len({1, 1, 2})) # 2 diff --git a/climbing-stairs/wozlsla.py b/climbing-stairs/wozlsla.py new file mode 100644 index 000000000..f61390341 --- /dev/null +++ b/climbing-stairs/wozlsla.py @@ -0,0 +1,104 @@ +""" +# Intuition +계단 정상에 오를 수 있는 방법의 수 - 1,2 - 순열? -> X. 자리수가 일정하지 않음 + +# Approach +접근 1) 1 or 2 로만 이동 가능 + +풀이 참고 +- 계단을 한 번에 최대 2칸 밖에 올라갈 수 없으므로, 3번째 칸에 발을 딛기 위해서는 바로 아래 칸인 2번째 칸이나 적어도 1번째 칸에 반드시 먼저 올라와있어야 함. +- 즉, n 칸에 발을 딛기위해서는 그 전에 n - 1 칸이나 n - 2 칸까지 올라와왔어야 한다. + +접근 2) +4 -> 5 +1 1 1 1 +2 1 1 +1 2 1 +1 1 2 +2 2 + +5 -> 8 +1 1 1 1 1 +2 2 1 +1 2 2 +2 1 2 +1 1 1 2 +1 1 2 1 +1 2 1 1 +2 1 1 1 + +n=5까지만 봤을 때 늘어나는 규칙이 피보나치수열과 같음. (hint) + +# Complexity +- Time complexity + - DP 1 : O(N) + - DP 2 : O(N) + + - Recursive 1 : O(2^N) + - Recursive 2 (caching): O(N) + +- Space complexity + - DP 1 : O(N) + - DP 2 : O(1) + + - Recursive 1 : O(N) + - Recursive 2 (caching): O(N) + +""" + + +# DP 2 (공간 최적화) +class Solution: + def climbStairs(self, n: int) -> int: + + if n < 3: + return n + + pre, cur = 1, 2 + + for _ in range(n - 2): + pre, cur = cur, pre + cur # 순서 ! + + return cur + + +""" DP 1 +class Solution: + def climbStairs(self, n: int) -> int: + + dp = {1:1, 2:2} + + for i in range(3, n+1): + dp[i] = dp[i-1] + dp[i-2] + + return dp[n] +""" + +""" Memoization (재귀 + 캐싱) +class Solution: + def climbStairs(self, n: int) -> int: + memo = {} + + def _climb(n): + + if n not in memo: + if n < 3: + memo[n] = n + else: + memo[n] = _climb(n - 1) + _climb(n - 2) + + return memo[n] + + return _climb(n) +""" + + +""" Recursive +class Solution: + def climbStairs(self, n: int) -> int: + + if n < 3: + return n + + return self.climbStairs(n-1) + self.climbStairs(n-2) +""" diff --git a/house-robber/wozlsla.py b/house-robber/wozlsla.py new file mode 100644 index 000000000..47093b4ac --- /dev/null +++ b/house-robber/wozlsla.py @@ -0,0 +1,67 @@ +""" +# Intuition + + +# Approach + + +# Complexity +- Time complexity: + +- Space complexity: + +""" + + +# DP +class Solution: + def rob(self, nums: List[int]) -> int: + + # initialize dp array + dp = [0] * (len(nums) + 1) + + if nums: + dp[1] = nums[0] + + for n in range(2, len(dp)): + # current house : nums[n-1] + rob_current = nums[n - 1] + dp[n - 2] + skip_current = dp[n - 1] + + dp[n] = max(rob_current, skip_current) + + return dp[-1] + + +""" Recursive + Memoization +class Solution: + def rob(self, nums: List[int]) -> int: + + memo = {} + + def dfs(start): + if start in memo: + return memo[start] + + if start >= len(nums): + memo[start] = 0 + else: + memo[start] = max(nums[start] + dfs(start + 2), dfs(start + 1)) + + return memo[start] + + return dfs(0) +""" + +""" Recursive +class Solution: + def rob(self, nums: List[int]) -> int: + + def dfs(start): + if start >= nums(len): + return 0 + + return max(nums[start] + dfs[start+2], dfs[start+1]) + + return dfs(0) +""" diff --git a/product-of-array-except-self/wozlsla.py b/product-of-array-except-self/wozlsla.py new file mode 100644 index 000000000..f4b426bb3 --- /dev/null +++ b/product-of-array-except-self/wozlsla.py @@ -0,0 +1,82 @@ +""" +# Intuition +하나씩 순회하면서 나머지 값들을 곱함 + +# Approach +접근 1) 원형 큐를 사용하면? +현재 제외하려는 요소를 popleft()로 제거하고, 나머지 요소들의 곱을 계산한 뒤, 다시 그 요소를 append()하여 다음 반복을 위해 덱의 순서를 회전 + +접근 2) +1 b c d --> 1 x bcd +a 1 c d --> a x cd +a b 1 d --> ab x d +a b c 1 --> abc x 1 + + +# Complexity +- Time complexity : O(N) +- Space complexity : O(N) / O(1) +""" + +from typing import List + + +class Solution: + def productExceptSelf(self, nums: List[int]) -> List[int]: + + n = len(nums) + res = [1] * n + + prefix_product = 1 + for i in range(n): + res[i] = prefix_product + prefix_product *= nums[i] + + suffix_product = 1 + for i in range(n - 1, -1, -1): + res[i] *= suffix_product + suffix_product *= nums[i] + + +""" +class Solution: + def productExceptSelf(self, nums: List[int]) -> List[int]: + + # 중복 계산을 피하는 방법? + res_1 = [1] * len(nums) + res_2 = [1] * len(nums) + + for n in range(1, len(nums)): # [1, 1(a), 2(ab), 6(abc)] + res_1[n] = res_1[n - 1] * nums[n - 1] + + # reverse X -> range를 반대로 + for n in range(len(nums) - 1, -1, -1): # [1, 4(d), 12(cd), 24(bcd)] + res_2[n] = res_2[n + 1] * nums[n + 1] + + res_2.reverse() + return [res_1[i] * res_2[i] for i in range(len(nums))] + + +sol = Solution() +print(sol.productExceptSelf([1, 2, 3, 4])) +""" + + +""" +# deque : O(N^2) +from collections import deque +import math + +class Solution: + def productExceptSelf(self, nums: List[int]) -> List[int]: + + nums = deque(nums) + result = [] + + for _ in range(len(nums)): # O(N) + tmp = nums.popleft() + result.append(math.prod(nums)) # O(N) + nums.append(tmp) + + return result +""" diff --git a/valid-anagram/wozlsla.py b/valid-anagram/wozlsla.py new file mode 100644 index 000000000..415105a13 --- /dev/null +++ b/valid-anagram/wozlsla.py @@ -0,0 +1,47 @@ +""" +# Intuition +애너그램 : 순서만 변경. 요소/개수 동일 -> 비교하여 같은지 확인 + +# Approach +해시테이블의 키-값(요소-개수) 쌍으로 저장 +- 직접 순회 : python interpreter 실행 및 get 메서드 반복 호출/업데이트 +- Counter : 문자열을 순회하며 각 문자의 빈도수를 계산하는 과정이 최적화(C)되어 있기 때문에, 파이썬으로 직접 반복문을 작성하는 것보다 훨씬 빠르게 동작 + +# Complexity +- Time complexity: O(N+M) +- Space complexity: O(K). 사실상 O(1) -> 영어 알파벳 문자열의 경우 K는 26으로 상수에 가까움 + +""" + +from collections import Counter + + +class Solution: + def isAnagram(self, s: str, t: str) -> bool: + return Counter(s) == Counter(t) + + +""" +class Solution: + def isAnagram(self, s: str, t: str) -> bool: + + # table_s = table_t = {} # '동일 객체를 참조'하기 때문에 가변 객체의 경우 수정 시 모두 반영 됨. + table_s = {} + table_t = {} + + for i in s: + table_s[i] = table_s.get(i, 0) + 1 + for e in t: + table_t[e] = table_t.get(e, 0) + 1 + + return table_s == table_t + +s = "anagram" +t = "nagaram" + +# print(Solution.isAnagram(s, t)) -> 클래스 메서드처럼 호출하고 있어서 self를 자동 전달하지 못함 + +# 클래스 인스턴스화 필요 +sol = Solution() +print(sol.isAnagram(s, t)) +"""