diff --git a/3sum/Seoya0512.py b/3sum/Seoya0512.py new file mode 100644 index 000000000..fc0013ca6 --- /dev/null +++ b/3sum/Seoya0512.py @@ -0,0 +1,78 @@ +from typing import List + +''' +1차 시도: 실패 (시간 초과) +- nums에서 두 수 (A,B)를 고른 후, 세번째 숫자인 -(A+B)가 A,B를 제외한 nums에 존재하는지 확인 +- 중복된 결과를 제거하기 위해 set과 sorted(tuple)을 활용 + +시간 복잡도: O(N^3) +- Outer loop와 Inner loop 각각 O(N) 이므로 O(N^2)은 기본 +- nums[j:]에서 third_num을 찾는 데 O(N) 시간이 걸려서 전체 시간 복잡도는 O(N^3) + +공간 복잡도: 최대 O(N^2) +- answer_set은 중복되지 않는 triplet을 저장하며, 3SUM 특성상 유니크한 triplet의 최대 개수는 O(N^2) + +''' + +class Solution: + def threeSum(self, nums: List[int]) -> List[List[int]]: + answer_set = set() + + for i in range(len(nums) - 1): + for j in range(i + 1, len(nums)): + third_num = -1 * (nums[i] + nums[j]) + + if third_num in nums[j:]: + answer_set.add(tuple(sorted([nums[i], nums[j], third_num]))) + + return [list(t) for t in answer_set] + +''' +개선 : if third_num in nums[j:] 부분을 set을 활용 +Approach +- nums에서 두 수 (A,B)를 고른 후, 세번째 숫자인 -(A+B)가 존재하는지 확인하는 기존 기조는 유지함 +- (A,B) 쌍 중 ‘A(첫 번째 숫자)’가 동일한 값일 때, + 이미 동일한 first 값으로 생성할 수 있는 모든 triplet을 처리했으므로 + 다시 같은 first로 시작하는 조합을 만들 필요가 없다는 점을 활용 +- seen set을 활용해서 (A,B)가 고정된 이후 세번째 숫자인 -(A+B)가 존재하는지 O(1) 시간에 확인 + +- (예시) seen set은 i가 고정된 이후 j를 순회하면서 nums[j] 값을 저장 +- nums = [a, b, c, d, e] + - i = 0 (nums[i] = a)일 때, seen = {} + - j = 1 (nums[j] = b)일 때, seen = {b} + - j = 2 (nums[j] = c)일 때, seen = {b, c} + - j = 3 (nums[j] = d)일 때, seen = {b, c, d} + - j = 4 (nums[j] = e)일 때, seen = {b, c, d, e) + +시간 복잡도: O(N^2) +- Outer loop와 Inner loop 각각 O(N) 이므로 전체 시간 복잡도는 O(N^2) + +공간 복잡도: 최대 O(N^2) +- seen set은 nums[j]를 저장하며 최악의 경우 O(N) +- used_first도 distinct first 값들을 저장하며 최대 O(N) +- answer_set은 중복되지 않는 triplet을 저장하며, 3SUM 특성상 유니크한 triplet의 최대 개수는 O(N^2) +''' +class Solution: + def threeSum(self, nums: List[int]) -> List[List[int]]: + answer_set = set() + n = len(nums) + + used_first = set() # 이미 i로 사용한 값들 + + for i in range(n - 1): + # 같은 값을 첫 번째 숫자로 한 번만 쓰기 + if nums[i] in used_first: + continue + used_first.add(nums[i]) + + seen = set() + for j in range(i + 1, n): + third_num = -1 * (nums[i] + nums[j]) + + if third_num in seen: + triplet = tuple(sorted([nums[i], nums[j], third_num])) + answer_set.add(triplet) + else: + seen.add(nums[j]) + + return [list(t) for t in answer_set] diff --git a/climbing-stairs/Seoya0512.py b/climbing-stairs/Seoya0512.py new file mode 100644 index 000000000..746242fe7 --- /dev/null +++ b/climbing-stairs/Seoya0512.py @@ -0,0 +1,17 @@ +''' +Approach: Dynamic Programming +- 혼자서 해결하려고 고민을 30분 이상 했으나 실패해서 알고달레 선생님의 블로그 도움을 받았습니다. +- 해당 문제를 통해 동적 계획,Dynamic Programming +- 더 적은 입력에 대한 답을 먼저 구해서 저장해놓고, 더 큰 입력에 대한 답을 구할 때 활용하는 방식에 대해 학습할 수 있었습니다! + +Time Complexity: O(n) +- for loop이 n번 순회하며 각 값에 대한 연산 O(n) 발생 +Space Complexity: O(n) +- nums 딕셔너리에 각 계단 수에 대한 경우의 수를 저장 +''' +class Solution: + def climbStairs(self, n: int) -> int: + nums = {1:1, 2:2} + for i in range(3, n+1): + nums[i] = nums[i-1] + nums[i-2] + return nums[n] diff --git a/product-of-array-except-self/Seoya0512.py b/product-of-array-except-self/Seoya0512.py new file mode 100644 index 000000000..386e0bb43 --- /dev/null +++ b/product-of-array-except-self/Seoya0512.py @@ -0,0 +1,28 @@ +''' +Approach +- 배열에 0이 2개 이상 있는 경우, 모든 원소는 0이 되므로 바로 답을 반환 +- "self(전체 곱에서 제외될 값)"의 인덱스를 기준으로 왼쪽(before)과 오른쪽(after)곱을 미리 계산해서 배열에 저장 +- 최종적으로 before와 after 배열의 같은 인덱스 값을 곱해서 반환 + +Time Complexity: O(N) +- after, before 배열을 각각 한 번씩 순회하며 계산 +- 결과를 반환할때 두 배열을 곱을 계산 +Space Complexity: O(N) +- N은 num의 길이이며 N크기에 비례하는 before, after 배열 생성 공간 +''' +from typing import List + +class Solution: + def productExceptSelf(self, nums: List[int]) -> List[int]: + if nums.count(0) > 1: + return [0] * len(nums) + + before = [1] * len(nums) + for idx in range(len(nums)-1): + before[idx+1] = before[idx] * nums[idx] + + after = [1] * len(nums) + for jdx in range(len(nums)-1, 0, -1): + after[jdx-1] = after[jdx] * nums[jdx] + + return [x * y for x, y in zip(before, after)] diff --git a/valid-anagram/Seoya0512.py b/valid-anagram/Seoya0512.py new file mode 100644 index 000000000..7e8e5ab68 --- /dev/null +++ b/valid-anagram/Seoya0512.py @@ -0,0 +1,50 @@ +''' +Approach 1: Sorting +- 제일 먼저 생각나는 간단한 방법은 sorted 함수를 활용해서 분류해서 s 와 t가 동일한지 확인하는 방법입니다. +- 정렬메소드를 사용하면 n log n의 시간복잡도가 발생하는 것을 인지하고도 풀어봤습니다. + +Time Complexity: O(n log n) +- 분류 정렬에서 O(n log n) 발생 + +Space Complexity: O(n) +- 분류 문자열을 생성하는 공간 때문에 O(n) 발생 +''' +class Solution: + def isAnagram(self, s: str, t: str) -> bool: + sorted_s = ''.join(sorted(s)) + sorted_t = ''.join(sorted(t)) + if(sorted_s == sorted_t): + return True + else: + return False + +''' +Approach 2: 빈도수 카운팅 +- 두 문자열의 길이를 먼저 비교해서 다르면 False를 반환해서 1차 필터링 +- count 딕셔너리를 생성해서 s 문자열의 빈도수를 카운팅 +- t 문자열을 순회하면서 딕셔너리에 포함되어 있으면 카운팅을 감소시키고, 값이 없으면 False를 반환 + + +Time Complexity: O(n) +- 두 문자열을 각각 순회하면 딕셔너리 생성, 빈도수 카운팅 및 감소 에서 O(n) 발생 + +Space Complexity: O(n) +- count 딕셔너리 저장 공간 때문에 O(n) 발생 +''' +class Solution: + def isAnagram(self, s: str, t: str) -> bool: + # 길이가 일치하지 않으면 False + if len(s) != len(t): + return False + # 문자 개수 카운트 + count = {} + for val in s: + count[val] = count.get(val, 0) + 1 + for val in t: + if val not in count: + return False + count[val] -= 1 + # 예시 : s = "aacc", t = "ccac" 인 경우 count[val] 이 음수가 될 수 있음 + if count[val] < 0: + return False + return True