diff --git a/combination-sum/taurus09318976.py b/combination-sum/taurus09318976.py new file mode 100644 index 000000000..e867fa04b --- /dev/null +++ b/combination-sum/taurus09318976.py @@ -0,0 +1,71 @@ +#이 문제는 1)현재 합이 target과 같으면 결과에 추가 후 되돌아감, 2) 현재 합이 target을 초과하면 더 이상 진행하지 않고 되돌아감, +#3) 각 candidate에 대해 recursive하게 시도 + +class Solution: + def combinationSum(self, candidates: List[int], target: int): + #결과를 저장할 리스트 + result = [] + + #start: 현재 선택할 수 있는 candidate의 시작 인덱스 + #current: 현재까지 선택한 숫자들의 리스트 + #current_sum: 현재까지 선택한 숫자들의 합 + def backtrack(start, current, current_sum): + #현재 합이 target과 같으면 현재 조합을 결과에 추가 후 되돌아감 + if current_sum == target: + result.append(current.copy()) + return + #현재 합이 target을 초과하면 더 이상 진행하지 않고 되돌아감 + if current_sum > target: + return + + #start 파라미터를 사용하여 중복 조합이 생기지 않도록 이전에 선택한 숫자부터 다시 시작 + for i in range(start, len(candidates)): + #현재 candidate 선택 + current.append(candidates[i]) + #recursive 호출 : i를 그대로 전달하여 같은 숫자를 무한반복하여 사용할 수 있음. + backtrack(i, current, current_sum + candidates[i]) + #마지막에 선택한 숫자 제거 + current.pop() + + #백트래킹 시작 + backtrack(0, [], 0) + #모든 가능한 조합 반환 + return result + + +# 테스트 케이스 +solution = Solution() + +# 테스트 1 +test1_candidates = [2,3,6,7] +test1_target = 7 +print(f"Expected: [[2,2,3],[7]]") +print(f"Result: {solution.combinationSum(test1_candidates, test1_target)}") + +# 테스트 2 +test2_candidates = [2,3,5] +test2_target = 8 +print(f"Expected: [[2,2,2,2],[2,3,3],[3,5]]") +print(f"Result: {solution.combinationSum(test2_candidates, test2_target)}") + +# 테스트 3 +test3_candidates = [2] +test3_target = +print(f"Expected: []") +print(f"Result: {solution.combinationSum(test3_candidates, test3_target)}") + + +#시간 복잡도: O(2^target) + #각 단계에서 숫자를 선택하거나 선택하지 않는 두 가지 선택지가 있음 + #최악의 경우, target을 만들기 위해 모든 가능한 조합을 시도해야 함. + #예를 들어 candidates = [1], target = 10일 때: + #[1,1,1,1,1,1,1,1,1,1]까지 시도해야 함 + #이는 2^10에 가까운 경우의 수가 발생 +#공간 복잡도: O(target) + #각 재귀 호출마다 새로운 스택 프레임이 생성됨 + #최대 깊이는 target을 가장 작은 숫자로 나눈 값임 + #예를 들어 target = 7, 리스트 내 가장 작은 숫자 = 2일 때 + #7/2 = 3.5 → 최대 3번의 재귀 호출 + #[2,2,3]을 만들 때 3번의 재귀 호출 + #공간 복잡도는 target 값에 비례하여 증가하므로 O(target)임 + #이는 target이 커질수록 더 많은 메모리가 필요하다는 것을 의미함 diff --git a/decode-ways/taurus09318976.py b/decode-ways/taurus09318976.py new file mode 100644 index 000000000..d02dcd833 --- /dev/null +++ b/decode-ways/taurus09318976.py @@ -0,0 +1,80 @@ +#계단 오르기 문제, 피보나치 수열과 동일한 패턴의 동적 프로그래밍 문제임 +#차이점은 피보나치 수열은 0,1로 시작하며 나머지 두 문제는 1,1로 시작한다는 점 +#디코딩 문제는 1)0이 아니어야 하며, 2)두 자리 숫자가 10~26 사이여야 한다는 조건이 주어진다는 점이 다름 + +class Solution: + def numDecodings(self, s: str): + #초기 예외값 처리. 빈 문자열이거나, 첫 문자가 0이면 디코딩 불가능 + if not s or s[0] == '0': + return 0 + + #DP 변수 초기화 + ##dp[i-2] 역할 + prev = 1 + ##dp[i-1] 역할 + curr = 1 + + #DP 계산 + ##첫번째 인덱스부터 마지막까지 + for i in range(1, len(s)): + ##현재 위치의 디코딩 방법 수를 임시로 저장 + temp = 0 + + ##한 자리 숫자로 디코딩하는 경우 + ###현재 숫자가 0이 아니면 + if s[i] != '0': + ###이전 방법 수를 더함 + temp += curr + + ##두 자리 숫자로 디코딩하는 경우 + ###현재와 이전 숫자를 합친 두 자리 수가 10~26 사이면 + if 10 <= int(s[i-1:i+1]) <= 26: + ###두 자리 전 방법 수를 더함 + temp += prev + + ##변수 업데이트 + ###이전 값을 두 자리 전 값으로 이동 + prev = curr + ###현재 값을 새로 계산된 값으로 이동 + curr = temp + + #최종 디코딩 방법 수 + return curr + +# 테스트 케이스 +solution = Solution() + +# 테스트 1 +test1 = "12" +print(f"Expected: 2") +print(f"Result: {solution.numDecodings(test1)}") + +# 테스트 2 +test2 = "226" +print(f"Expected: 3") +print(f"Result: {solution.numDecodings(test2)}") + +# 테스트 3 +test3 = "06" +print(f"Expected: 0") +print(f"Result: {solution.numDecodings(test3)}") + + +#시간 복잡도: O(n) + #n = 입력 문자열 s의 길이 + #for 루프가 문자열의 길이만큼 한 번 실행됨 + #각 반복에서 수행하는 연산: + #s[i] != '0' 비교: O(1) + #int(s[i-1:i+1]) 변환: O(1) + #10 <= two_digit <= 26 비교: O(1) + #변수 업데이트: O(1) + #따라서 전체 시간 복잡도는 O(n) +#공간 복잡도: O(1) + #추가로 사용하는 공간: + #prev: O(1) + #curr: O(1) + #temp: O(1) + #i: O(1) + #입력 크기 n과 무관하게 상수 개수의 변수만 사용 + #따라서 공간 복잡도는 O(1) + diff --git a/maximum-subarray/taurus09318976.py b/maximum-subarray/taurus09318976.py new file mode 100644 index 000000000..cd69ce64f --- /dev/null +++ b/maximum-subarray/taurus09318976.py @@ -0,0 +1,55 @@ +#이 문제는 카데인 알고리즘(Kadane's Algorithm) 문제로 +#원래 배열에서 연속된 요소들로만 구성된 최대 부분 배열 합을 찾는 문제임 + +class Solution: + def maxSubArray(self, nums: List[int]): + #max_sum: 지금까지 발견한 가장 큰 부분 배열의 합 + #current_sum: 현재 고려 중인 부분 배열의 합 + #위의 둘을 첫번째 요소로 초기화 + max_sum = current_sum = nums[0] + + #두 번째 요소부터 시작 + for num in nums[1:]: + #현재 숫자를 포함한 새로운 부분 배열의 합 계산 + current_sum = max(num, current_sum + num) + #num: 현재 숫자만으로 새로운 부분 배열 시작 + #current_sum + num: 기존 부분 배열에 현재 숫자 추가 + + #최대 합 업데이트 + max_sum = max(max_sum, current_sum) + + return max_sum + +# 테스트 케이스 +solution = Solution() + +# 테스트 1 +test1 = [-2,1,-3,4,-1,2,1,-5,4] +print(f"Expected: 6") +print(f"Result: {solution.maxSubArray(test1)}") + +# 테스트 2 +test2 = [1] +print(f"Expected: 1") +print(f"Result: {solution.maxSubArray(test2)}") + +# 테스트 3 +test3 = [5,4,-1,7,8] +print(f"Expected: 23") +print(f"Result: {solution.maxSubArray(test3)}") + + +#시간 복잡도: O(n) + #n = nums의 길이 + #for 루프가 배열의 길이만큼 한 번 실행됨 + #각 반복에서 수행하는 연산: + #max(num, current_sum + num): O(1) + #max(max_sum, current_sum): O(1) + #따라서 전체 시간 복잡도는 O(n)임 +#공간 복잡도: O(1) + #추가로 사용하는 공간: + #max_sum: O(1) + #current_sum: O(1) + #num: O(1) + #입력 크기 n과 무관하게 상수 개수의 변수만 사용 + #따라서 공간 복잡도는 O(1)임 diff --git a/number-of-1-bits/taurus09318976.py b/number-of-1-bits/taurus09318976.py new file mode 100644 index 000000000..721d877d3 --- /dev/null +++ b/number-of-1-bits/taurus09318976.py @@ -0,0 +1,50 @@ +#이 문제는 1) 주어진 정수를 이진수로 변환, 2) 변환된 이진수에서 1의 개수를 세기 순서로 해결해야 함 + +class Solution: + def hammingWeight(self, n: int): + count = 0 + while n: + #n의 가장 오른쪽 비트가 1인지 확인, 1이면 count를 증가시킴 + if n & 1 == 1 : + count += 1 + #다음 비트를 검사하기 위해 n을 오른쪽으로 1비트씩 시프트함 + n = n >> 1 + return count + + + +# 테스트 케이스 +solution = Solution() + +# 테스트 1 +test1 = 11 +print(f"Expected: 3") +print(f"Result: {solution.hammingWeight(test1)}") + +# 테스트 2 +test2 = 128 +print(f"Expected: 1") +print(f"Result: {solution.hammingWeight(test2)}") + +# 테스트 3 +test3 = 2147483645 +print(f"Expected: 30") +print(f"Result: {solution.hammingWeight(test3)}") + + +#cf. n & 0일 때 예: n = 1011 +# 1011 +#& 0000 +#----------- +# 0000 -> 0과 AND 연산을 하면 어떤 수든지 결과가 항상 0이 됨 +#반면에 n & 1을 사용하는 경우 1의 이진수 표현이 0001이기 때문에 +#이 연산으로 n의 가장 오른쪽 비트만 확인할 수 있음. + +#시간 복잡도 분석: O(log n) + #while n 루프는 1의 개수와는 무관하게, n의 이진수 자릿수만큼 반복됨. + #예: n = 128 (10000000)일 때 8번 반복 + #일반적으로 n의 이진수 자릿수는 log₂n + #n & 1: O(1), n >> 1: O(1)의 경우 상수 시간만큼 걸림 + +#공간 복잡도 분석: O(1) + #count 변수의 공간복잡도는 O(1)이며, n을 수정하면서 사용하므로 추가적인 배열이나 리스트가 필요하지 않음 diff --git a/valid-palindrome/taurus09318976.py b/valid-palindrome/taurus09318976.py new file mode 100644 index 000000000..cdb32f9dd --- /dev/null +++ b/valid-palindrome/taurus09318976.py @@ -0,0 +1,48 @@ +#이 문제를 해결하기 위해서는 1) 문자열을 소문자로 변환, 2) 알파벳과 숫자만 남기고 다른 문자 제거, +#3) 문자열이 palindrome인지 확인 + +class Solution: + def isPalindrome(self, s: str): + # 빈 리스트 extracted_string 생성 + extracted_string = [] + + + for i in s: + #isalnum() 메서드를 사용해 알파벳과 숫자만 필터링함 + if i.isalnum(): + #조건에 맞으면, lower() 메서드로 문자열을 소문자로 변환하고 알파벳과 숫자만 남김 + extracted_string.append(i.lower()) + #extracted_string 리스트의 모든 요소를 ''제거 후 하나의 문자열로 결합 + extracted_string = ''.join(extracted_string) + + # palindrome인지 확인 + return extracted_string == extracted_string[::-1] + +# 테스트 케이스 +solution = Solution() + +# 테스트 1 +test1 = "A man, a plan, a canal: Panama" +print(f"Expected: True") +print(f"Result: {solution.isPalindrome(test1)}") + +# 테스트 2 +test2 = "race a car" +print(f"Expected: False") +print(f"Result: {solution.isPalindrome(test2)}") + +# 테스트 3 +test3 = " " +print(f"Expected: True") +print(f"Result: {solution.isPalindrome(test3)}") + + +#시간 복잡도 : O(n), 입력 문자열 s의 모든 문자를 한 번씩 순회함(n = 입력 문자열의 길이) + #for c in s 루프: O(n). 입력 문자열 s의 모든 문자를 한 번씩 순회하므로 O(n) + #isalnum(), lower(), append()의 경우 : O(1), 각 문자에 대해 상수 시간이 걸림 + #'filtered == filtered[::-1]', '.join(filtered)'의 경우 : O(m) (m = 필터링된 문자열의 길이) + #m ≤ n, 최악의 경우 m = n + +#공간 복잡도 분석: O(n), 모든 문자가 알파벳이나 숫자인 최악의 경우 m = n + #filtered 리스트: O(m) + #filtered[::-1]: O(m)