diff --git a/find-minimum-in-rotated-sorted-array/KwonNayeon.py b/find-minimum-in-rotated-sorted-array/KwonNayeon.py new file mode 100644 index 000000000..bef237e1b --- /dev/null +++ b/find-minimum-in-rotated-sorted-array/KwonNayeon.py @@ -0,0 +1,42 @@ +""" +Constraints: +- n == nums.length +- 1 <= n <= 5000 +- -5000 <= nums[i] <= 5000 +- All the integers of nums are unique. +- nums is sorted and rotated between 1 and n times. + +Time Complexity: O(log n) +- 이진 탐색을 사용하므로 매 단계마다 탐색 범위가 절반으로 줄어듦 + +Space Complexity: O(1) +- 추가 공간을 사용하지 않고 포인터만 사용 + +풀이방법: +1. 이진 탐색(Binary Search) 활용 +2. mid와 right 값을 비교하여 조건을 나눔 + - Case 1: nums[mid] > nums[right] + - 오른쪽 부분이 정렬되어 있지 않음 + - 최솟값은 mid 오른쪽에 존재 + - left = mid + 1 + - Case 2: nums[mid] <= nums[right] + - mid부터 right까지는 정렬되어 있음 + - 최솟값은 mid를 포함한 왼쪽에 존재 가능 + - right = mid +""" + +class Solution: + def findMin(self, nums: List[int]) -> int: + left = 0 + right = len(nums) - 1 + + while left < right: + mid = (left + right) // 2 + + if nums[mid] > nums[right]: + left = mid + 1 + + else: + right = mid + + return nums[left] diff --git a/linked-list-cycle/KwonNayeon.py b/linked-list-cycle/KwonNayeon.py new file mode 100644 index 000000000..13fa2d99d --- /dev/null +++ b/linked-list-cycle/KwonNayeon.py @@ -0,0 +1,58 @@ +""" +Constraints: +- The number of the nodes in the list is in the range [0, 10^4] +- -10^5 <= Node.val <= 10^5 +- pos is -1 or a valid index in the linked-list + +Time Complexity: +- Solution 1: O(n) +- Solution 2: O(n) + +Space Complexity: +- Solution 1: O(n) - visited set에 모든 노드를 저장할 수 있음 +- Solution 2: O(1) - 추가 메모리 사용하지 않음 + +풀이방법: +1. 처음엔 직관적인 방법으로 해결, 한 번 마주친 노드를 다시 만나는지를 체크하는 방식 +2. slow, fast 두 개의 노드를 활용, 만약 cycle이 존재하는 경우 fast가 slow와 언젠가 만나게 됨, + 만약 cycle이 없다면 둘은 만나지 않음 +""" + +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, x): +# self.val = x +# self.next = None + +# Solution 1: 한 번 마주친 노드를 다시 만나는지를 체크 +class Solution: + def hasCycle(self, head: Optional[ListNode]) -> bool: + visited = set() + + current = head + while current: + if current in visited: + return True + visited.add(current) + current = current.next + + return False + +# Solution 2: 두 개의 포인터 이용 +class Solution: + def hasCycle(self, head: Optional[ListNode]) -> bool: + if not head: + return False + + slow = head + fast = head + + while fast and fast.next: + slow = slow.next + fast = fast.next.next + + if slow == fast: + return True + + return False + diff --git a/maximum-product-subarray/KwonNayeon.py b/maximum-product-subarray/KwonNayeon.py new file mode 100644 index 000000000..ddef49b53 --- /dev/null +++ b/maximum-product-subarray/KwonNayeon.py @@ -0,0 +1,37 @@ +""" +Constraints: +- 1 <= nums.length <= 2 * 10^4 +- -10 <= nums[i] <= 10 +- The product of any subarray of nums is guaranteed to fit in a 32-bit integer. + +Time Complexity: +- O(n): 배열을 한 번만 순회하면서 각 위치에서 상수 시간 연산만 수행 + +Space Complexity: +- O(1): 고정된 추가 변수만 사용 (curr_max, curr_min, ...) + +풀이방법: +1. DP로 각 위치에서 가능한 최대곱과 최소곱을 동시에 추적함 +2. 각 위치에서 세 개의 선택지 존재: 새로 시작 vs 이전 최대곱과 곱하기 vs 이전 최소곱과 곱하기 +3. 최소곱이 필요한 이유: 나중에 음수를 만났을 때 최대값이 될 수 있기 때문 +4. 매 단계마다 result 업데이트 + +고려사항: +1. 값이 0인 경우 → 새로 시작해야 함 +2. temp 변수: curr_max를 업데이트하면 curr_min 계산 시 원래 값이 필요함 +""" +class Solution: + def maxProduct(self, nums: List[int]) -> int: + curr_max = nums[0] + curr_min = nums[0] + result = nums[0] + + for i in range(1, len(nums)): + temp = curr_max + curr_max = max(nums[i], curr_max * nums[i], curr_min * nums[i]) + curr_min = min(nums[i], temp * nums[i], curr_min * nums[i]) + result = max(result, curr_max) + + return result + + diff --git a/minimum-window-substring/KwonNayeon.py b/minimum-window-substring/KwonNayeon.py new file mode 100644 index 000000000..b6730723a --- /dev/null +++ b/minimum-window-substring/KwonNayeon.py @@ -0,0 +1,49 @@ +""" +Constraints: +- m == s.length +- n == t.length +- 1 <= m, n <= 10^5 +- s and t consist of uppercase and lowercase English letters. + +Time Complexity: O(s * t) +- for 루프로 s의 길이만큼 반복 +- while 루프 안의 all() 조건에서 t의 각 문자 비교 +- 따라서 O(s * t) + +Space Complexity: O(s + t) +- t_counts: O(t)의 공간 +- w_counts: O(s)의 공간 +- 따라서 O(s + t) + +풀이방법: +1. Counter로 t의 문자 빈도수 저장 +2. 슬라이딩 윈도우로 s 탐색: + - high 포인터로 윈도우를 확장하며 필요한 문자 찾기 + - 필요한 문자를 모두 찾으면 low 포인터로 윈도우 축소 +3. 최소 길이의 윈도우 반환 + +메모: +- 풀이 방법을 보고 익힘 +- 해시맵과 슬라이딩 윈도우 관련 다른 문제들을 풀고 이 문제 스스로 풀어보기 +""" +class Solution: + def minWindow(self, s: str, t: str) -> str: + + t_counts = Counter(t) + w_counts = Counter() + + min_low, min_high = 0, len(s) + low = 0 + + for high in range(len(s)): + w_counts[s[high]] += 1 + + while all(t_counts[ch] <= w_counts[ch] for ch in t_counts): + if high - low < min_high - min_low: + min_low, min_high = low, high + if s[low] in t_counts: + w_counts[s[low]] -= 1 + low += 1 + + return s[min_low : min_high + 1] if min_high < len(s) else "" + diff --git a/pacific-atlantic-water-flow/KwonNayeon.py b/pacific-atlantic-water-flow/KwonNayeon.py new file mode 100644 index 000000000..7e6e355d2 --- /dev/null +++ b/pacific-atlantic-water-flow/KwonNayeon.py @@ -0,0 +1,53 @@ +""" +Constraints: +- m == heights.length +- n == heights[r].length +- 1 <= m, n <= 200 +- 0 <= heights[r][c] <= 10^5 + +Time Complexity: O(m*n) +- 각 셀을 최대 한 번씩만 방문함 + +Space Complexity: O(m*n) +- visited sets(pacific, atlantic)가 최대 m*n 크기 +- 재귀 호출 스택도 최대 m*n 깊이 가능 + +풀이방법: +1. Pacific(좌측상단)과 Atlantic(우측하단)의 경계에서 시작함 +2. DFS로 현재 높이보다 높거나 같은 인접 셀로만 이동함 (물은 위 -> 아래로 흐르지만, 거꾸로 접근했으니까) +3. 각 바다에서 도달 가능한 셀들을 Set에 저장 +4. 두 Set의 교집합이 정답 (양쪽 바다로 모두 흐를 수 있는 지점들) +""" +class Solution: + def pacificAtlantic(self, heights: List[List[int]]) -> List[List[int]]: + if not heights: + return [] + + rows, cols = len(heights), len(heights[0]) + pacific = set() + atlantic = set() + + def dfs(r, c, visited): + visited.add((r, c)) + directions = [(1,0), (-1,0), (0,1), (0,-1)] + + for dr, dc in directions: + new_r, new_c = r + dr, c + dc + if (new_r < 0 or new_r >= rows or + new_c < 0 or new_c >= cols or + (new_r, new_c) in visited): + continue + if heights[new_r][new_c] >= heights[r][c]: + dfs(new_r, new_c, visited) + + for c in range(cols): + dfs(0, c, pacific) + for r in range(rows): + dfs(r, 0, pacific) + + for c in range(cols): + dfs(rows-1, c, atlantic) + for r in range(rows): + dfs(r, cols-1, atlantic) + + return list(pacific & atlantic)