diff --git a/linked-list-cycle/alphaorderly.py b/linked-list-cycle/alphaorderly.py new file mode 100644 index 0000000000..cf25087094 --- /dev/null +++ b/linked-list-cycle/alphaorderly.py @@ -0,0 +1,35 @@ + +""" +시간복잡도: O(n) +공간복잡도: O(1) + +1. 토끼와 거북이를 초기화한다. + - 토끼 : 두 칸씩 이동 + - 거북이 : 한 칸씩 이동 +2. 토끼와 거북이가 만날 때까지 이동한다. +3. 토끼와 거북이가 만나면 사이클이 있다고 판단한다. +4. 토끼와 거북이가 만나지 않으면 사이클이 없다고 판단한다. + +# 원리 # +- 사이클이 존재하지 않는다면 토끼와 거북이는 결국 끝에 도달한다. +- 사이클이 존재한다면 토끼와 거북이는 결국 사이클 내에서 만난다. +""" +class ListNode: + def __init__(self, x): + self.val = x + self.next = None + + +class Solution: + def hasCycle(self, head: Optional[ListNode]) -> bool: + hare = head + tortoise = head + + while hare and hare.next: + hare = hare.next.next + tortoise = tortoise.next + + if hare is tortoise: + return True + + return False diff --git a/maximum-product-subarray/alphaorderly.py b/maximum-product-subarray/alphaorderly.py new file mode 100644 index 0000000000..18ef35418c --- /dev/null +++ b/maximum-product-subarray/alphaorderly.py @@ -0,0 +1,32 @@ +""" +시간복잡도: O(n) +공간복잡도: O(1) + +- 만약 현재 숫자가 음수라면, 양수 최대값(pos)과 음수 최소값(neg)을 서로 교환한다. + - 이유: 음수 * 음수 = 양수이므로, 음수를 만나면 최대값/최소값 후보가 바뀔 수 있다. +- pos와 neg를 nums의 첫 번째 값으로 초기화한다. + - pos: 현재까지의 양수 곱셈 최대값 + - neg: 현재까지의 음수 곱셈 최소값 +- ans를 nums의 첫 번째 값으로 초기화한다. + - ans: 현재까지의 최대 곱셈 결과 +- nums의 두 번째 원소부터 끝까지 pos와 neg를 갱신한다. + - pos는 현재 숫자와 pos*현재 숫자 중 큰 값 + - neg는 현재 숫자와 neg*현재 숫자 중 작은 값 +- ans를 pos와 비교하여 최대값으로 갱신한다. +- 반복이 끝나면 ans를 반환한다. +""" +class Solution: + def maxProduct(self, nums: List[int]) -> int: + pos = neg = ans = nums[0] + N = len(nums) + + for i in range(1, N): + if nums[i] < 0: + neg, pos = pos, neg + + pos = max(nums[i], nums[i] * pos) + neg = min(nums[i], nums[i] * neg) + + ans = max(ans, pos) + + return ans diff --git a/minimum-window-substring/alphaorderly.py b/minimum-window-substring/alphaorderly.py new file mode 100644 index 0000000000..264680a8e5 --- /dev/null +++ b/minimum-window-substring/alphaorderly.py @@ -0,0 +1,64 @@ +""" + +시간복잡도: O(n) +공간복잡도: O(1) + +슬라이딩 윈도우 기법을 사용해 문자열 s에서 t의 모든 문자를 포함하는 최소 윈도우를 찾는다. + +Window 클래스는 타겟 문자열(t)에 구성된 각 문자별 필요한 개수를 카운트하고, +윈도우 내 해당 문자 개수를 관리하며, 현재 윈도우가 타겟을 만족하는지(check) 확인한다. + +add 메서드는 윈도우에 문자를 추가하여 개수를 갱신하고, + - 타겟의 문자 갯수와 같아지면 key_count를 증가시킨다. +remove 메서드는 윈도우에서 문자를 제거하여 개수를 줄인다. + - 타겟의 문자 갯수보다 작아지면 key_count를 감소시킨다. + +check 메서드는 윈도우에 타겟 문자가 필요한 만큼 모두 포함되어 있는지 검사한다. + - key_count와 target_count가 같으면 True를 반환한다. +""" +class Window: + def __init__(self, target: str): + self.target = Counter(target) + self.window = defaultdict(int) + + self.key_count = 0 + self.target_count = len(self.target) + + def check(self): + return self.key_count == self.target_count + + def add(self, ch: str): + self.window[ch] += 1 + + if self.window[ch] == self.target[ch]: + self.key_count += 1 + + def remove(self, ch: str): + self.window[ch] -= 1 + if self.window[ch] < self.target[ch]: + self.key_count -= 1 + + +class Solution: + def minWindow(self, s: str, t: str) -> str: + window = Window(t) + left = 0 + ans = (-float("inf"), float("inf")) + + for right, value in enumerate(s): + + window.add(value) + if not window.check(): + continue + + while window.check(): + window.remove(s[left]) + left += 1 + + if ans[1] - ans[0] > right - left: + ans = (left - 1, right) + + if ans[0] == -float("inf"): + return "" + + return s[ans[0] : ans[1] + 1] diff --git a/pacific-atlantic-water-flow/alphaorderly.py b/pacific-atlantic-water-flow/alphaorderly.py new file mode 100644 index 0000000000..dab1852476 --- /dev/null +++ b/pacific-atlantic-water-flow/alphaorderly.py @@ -0,0 +1,76 @@ +""" +시간복잡도: O(m * n) +공간복잡도: O(m * n) + +1. 태평양과 대서양 각각에서 물이 도달할 수 있는 위치를 기록할 2차원 배열(check)을 만든다. +2. 태평양과 맞닿은 칸들(왼쪽 열과 위쪽 행)에는 태평양 도달 가능(값 1), 대서양과 맞닿은 칸들(오른쪽 열과 아래쪽 행)에는 대서양 도달 가능(값 2) 표시를 한다. + - 1: 태평양, 2: 대서양 — 2진수로는 각각 01, 10이라는 의미임. +3. 각 바다에 인접한 칸들에서 시작해, BFS를 이용해 도달 가능한 모든 칸을 확장한다. + - 인접한 칸으로 이동할 때, 항상 지금 칸보다 높이가 같거나 더 높은 칸으로만 물이 흐를 수 있다(즉, 물이 거슬러 흐르는 조건). + - 이미 같은 바다에서 방문한 적이 있는 칸은 건너뛴다. + - 새로운 위치에 도달할 때마다 check 배열을 갱신하고 큐에 추가한다. +4. 태평양과 대서양에서 모두 도달 가능한 칸(값이 3이 된 칸)을 답으로 모아 반환한다. + +# 비트마스킹 기법 사용 원리 # +- 태평양과 대서양 두 바다에서 도달 가능한 칸을 표시하기 위해 비트마스킹 기법을 사용한다. +- 각 칸에 대해, 1비트(01)는 태평양(Pacific), 2비트(10)는 대서양(Atlantic) 도달 가능을 의미한다. +- 예를 들어 각 칸의 값이 1이면 태평양, 2이면 대서양, 3이면 두 바다 모두에 도달 가능한 칸임을 나타낸다. +- bfs/dfs로 인접 칸(상하좌우)로 확장할 때, 이미 해당 바다의 비트가 켜져 있으면 방문하지 않는다. +""" +class Solution: + def pacificAtlantic(self, heights: List[List[int]]) -> List[List[int]]: + ROW = len(heights) + COL = len(heights[0]) + DIR = [ + [1, 0], + [0, 1], + [-1, 0], + [0, -1] + ] + + check = [[0] * COL for _ in range(ROW)] + + for r in range(ROW): + check[r][0] |= 1 + check[r][COL - 1] |= 2 + + for c in range(COL): + check[0][c] |= 1 + check[ROW - 1][c] |= 2 + + queue = deque([]) + + for r in range(ROW): + for c in range(COL): + if check[r][c] != 0: + queue.append((r, c)) + + def bound(row: int, col: int) -> bool: + return 0 <= row < ROW and 0 <= col < COL + + while queue: + r, c = queue.popleft() + + for dr, dc in DIR: + tr, tc = r + dr, c + dc + + if not bound(tr, tc): + continue + + if heights[tr][tc] < heights[r][c]: + continue + + if check[tr][tc] | check[r][c] == check[tr][tc]: + continue + + check[tr][tc] |= check[r][c] + queue.append((tr, tc)) + + ans = [] + + for r in range(ROW): + for c in range(COL): + if check[r][c] == 3: + ans.append([r, c]) + + return ans diff --git a/sum-of-two-integers/alphaorderly.py b/sum-of-two-integers/alphaorderly.py new file mode 100644 index 0000000000..13e68ba20f --- /dev/null +++ b/sum-of-two-integers/alphaorderly.py @@ -0,0 +1,39 @@ +""" +시간복잡도: O(1) +공간복잡도: O(1) + +16비트 정수 범위에서 각 비트를 하나씩 확인하며 덧셈을 수행한다. + +각 비트의 합은 XOR 연산으로 구하고, +올림수(carry)는 두 비트 이상이 1인 경우를 OR 연산으로 계산한다. + - 전가산기 원리 적용 + +계산된 각 비트는 ans의 해당 위치에 저장한다. + +Python의 int는 고정된 비트 폭을 가지지 않으므로, +최종 결과의 최상위 비트가 1인 경우에는 16비트 음수로 직접 변환한다. + +MASK와 XOR하여 16비트 범위 안에서 비트를 반전한 뒤, +~ 연산을 적용하여 Python의 음수 정수 형태로 변환한다. +""" +class Solution: + def getSum(self, a: int, b: int) -> int: + MASK = 0xFFFF + CHECK = 0x8000 + + ans = carry = left = 0 + for i in range(16): + a_bit = a & 1 + b_bit = b & 1 + + left = a_bit ^ b_bit ^ carry + carry = (a_bit & b_bit) | (b_bit & carry) | (a_bit & carry) + + ans |= left * 2**i + a >>= 1 + b >>= 1 + + if ans & CHECK == 0: + return ans + else: + return ~(MASK ^ ans)