diff --git a/binary-tree-level-order-traversal/hi-rachel.py b/binary-tree-level-order-traversal/hi-rachel.py new file mode 100644 index 000000000..fd25b4374 --- /dev/null +++ b/binary-tree-level-order-traversal/hi-rachel.py @@ -0,0 +1,40 @@ +""" +주어진 이진 트리를 위에서 아래로, 왼쪽에서 오른쪽으로 레벨 단위로 순회하여 +각 레벨에 있는 노드들의 값을 리턴하는 문제 + +TC: O(N), 모든 노드를 한 번씩 방문 +SC: O(N), 큐와 결과 리스트에 최대 N개의 노드 저장 가능 +""" + +# Definition for a binary tree node. +class TreeNode: + def __init__(self, val=0, left=None, right=None): + self.val = val + self.left = left + self.right = right + +from collections import deque +from typing import Optional, List + +class Solution: + def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]: + if not root: + return [] + + output = [] + queue = deque([root]) + + while queue: + # 현재 레벨에 있는 모든 노드들의 값을 리스트에 담기 + level = [node.val for node in queue] + output.append(level) + + # 현재 레벨에 있는 모든 노드 탐색 + for _ in range(len(queue)): + node = queue.popleft() + if node.left: + queue.append(node.left) + if node.right: + queue.append(node.right) + + return output diff --git a/binary-tree-level-order-traversal/hi-rachel.ts b/binary-tree-level-order-traversal/hi-rachel.ts new file mode 100644 index 000000000..7c0034549 --- /dev/null +++ b/binary-tree-level-order-traversal/hi-rachel.ts @@ -0,0 +1,45 @@ +/** + * python의 len(queue)는 반복 시작 전에 계산된 값, 즉 고정된 횟수만큼 반복 + * JS의 queue.length는 반복마다 실시간으로 다시 계산하므로 고정 사이즈 size 변수 사용 + */ + +/** + * Definition for a binary tree node. + */ +class TreeNode { + val: number; + left: TreeNode | null; + right: TreeNode | null; + constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) { + this.val = val === undefined ? 0 : val; + this.left = left === undefined ? null : left; + this.right = right === undefined ? null : right; + } +} + +function levelOrder(root: TreeNode | null): number[][] { + if (!root) return []; + + const queue: TreeNode[] = [root]; + const output: number[][] = []; + + while (queue.length > 0) { + const level: number[] = []; + for (let node of queue) { + level.push(node.val); + } + output.push(level); + const size = queue.length; + + for (let i = 0; i < size; i++) { + const node = queue.shift(); + if (node && node.left) { + queue.push(node.left); + } + if (node && node.right) { + queue.push(node.right); + } + } + } + return output; +} diff --git a/counting-bits/hi-rachel.py b/counting-bits/hi-rachel.py new file mode 100644 index 000000000..4c02ac6dd --- /dev/null +++ b/counting-bits/hi-rachel.py @@ -0,0 +1,49 @@ +""" +정수 n이 주어졌을 때, 0부터 n까지의 모든 수에 대해 각 수를 이진수로 표현했을 때 1의 개수를 구하는 문제 + +TC: O(N log N), 모든 수 순환 + 이진수 변환 과정 (log i) +SC: O(N) +""" + +from typing import List + +# 처음 풀이 +class Solution: + def countBits(self, n: int) -> List[int]: + def countOne(num): + cnt = 0 + while True: + rest = num % 2 + if rest == 1: + cnt += 1 + num //= 2 + if num <= 1: + if num == 1: + cnt += 1 + break + return cnt + + result = [] + for i in range(0, n + 1): + result.append(countOne(i)) + + return result + +""" +DP 풀이 - 시간 복잡도 개선 + +bits[i] = bits[i >> 1] + (i &) +i >> 1 은 i를 오른쪽으로 1비트 이동 -> i를 2로 나눈 값 +i & 1 은 i의 마지막 1비트가 1인지 확인, 짝수면 0, 홀수면 1 +i의 1의 개수 = i를 2로 나눈 수의 1의 개수 + 마지막 비트가 1인지 여부 + +TC: O(N) +SC: O(N) +""" + +class Solution: + def countBits(self, n: int) -> List[int]: + dp = [0] * (n + 1) + for i in range(1, n + 1): + dp[i] = dp[i >> 1] + (i & 1) + return dp diff --git a/counting-bits/hi-rachel.ts b/counting-bits/hi-rachel.ts new file mode 100644 index 000000000..26f8404a0 --- /dev/null +++ b/counting-bits/hi-rachel.ts @@ -0,0 +1,8 @@ +function countBits(n: number): number[] { + const result: number[] = new Array(n + 1).fill(0); + + for (let i = 0; i <= n; i++) { + result[i] = result[i >> 1] + (i & 1); + } + return result; +} diff --git a/house-robber-ii/hi-rachel.py b/house-robber-ii/hi-rachel.py new file mode 100644 index 000000000..fe8015882 --- /dev/null +++ b/house-robber-ii/hi-rachel.py @@ -0,0 +1,37 @@ +""" +이웃집은 못텀, 주어진 nums list 집은 원형으로 이어져 있음 (맨 처음 <- > 맨 마지막 이웃) +가장 많은 돈을 가지고 있는 집을 최대한으로 턴 총 금액을 반환 + +1. 첫 집을 포함하고, 마지막 집을 제외하는 경우 nums[:-1] +2. 첫 집을 제외하고, 마지막 집을 포함하는 경우 nums[1:] + +TC: O(N), +SC: O(N) +""" + +from typing import List + +class Solution: + def rob(self, nums: List[int]) -> int: + if len(nums) == 0: + return 0 + if len(nums) == 1: + return nums[0] + if len(nums) == 2: + return max(nums) + + def rob_linear(houses: List[int]) -> int: + if len(houses) == 1: + return houses[0] + + dp = [0] * len(houses) + dp[0] = houses[0] + dp[1] = max(houses[0], houses[1]) + for i in range(2, len(houses)): + dp[i] = max(dp[i - 1], houses[i] + dp[i - 2]) + return dp[-1] + + case1 = rob_linear(nums[:-1]) + case2 = rob_linear(nums[1:]) + + return max(case1, case2) diff --git a/house-robber-ii/hi-rachel.ts b/house-robber-ii/hi-rachel.ts new file mode 100644 index 000000000..a7f493725 --- /dev/null +++ b/house-robber-ii/hi-rachel.ts @@ -0,0 +1,20 @@ +function rob(nums: number[]): number { + if (nums.length === 1) return nums[0]; + if (nums.length === 2) return Math.max(nums[0], nums[1]); + + function rob_linear(nums: number[]): number { + const size = nums.length; + const dp = new Array(size).fill(0); + dp[0] = nums[0]; + dp[1] = Math.max(nums[0], nums[1]); + for (let i = 2; i < size; i++) { + dp[i] = Math.max(nums[i] + dp[i - 2], dp[i - 1]); + } + return dp[dp.length - 1]; + } + + return Math.max( + rob_linear(nums.slice(0, nums.length - 1)), + rob_linear(nums.slice(1)) + ); +} diff --git a/meeting-rooms-ii/hi-rachel.py b/meeting-rooms-ii/hi-rachel.py new file mode 100644 index 000000000..7a1d469e2 --- /dev/null +++ b/meeting-rooms-ii/hi-rachel.py @@ -0,0 +1,79 @@ +""" +https://neetcode.io/problems/meeting-schedule-ii + +주어지는 회의 시간 리스트 intervals = [[s₁, e₁], [s₂, e₂], ...] (각 si < ei)에 대해, +동시에 진행되는 회의의 최대 개수, 즉 필요한 최소 회의실 개수를 계산하는 문제 + +TC: O(N log N), 회의 시간 정렬 O(N log N) +SC: O(N), 배열 사용 +""" + +#Definition of Interval: +class Interval(object): + def __init__(self, start, end): + self.start = start + self.end = end + +from typing import List + +class Solution: + def minMeetingRooms(self, intervals: List[List[int]]) -> int: + if not intervals: + return 0 + + # 회의 시작 시간과 종료 시간을 저장하는 배열 + start_times = [interval.start for interval in intervals] + end_times = [interval.end for interval in intervals] + + # 시작 시간과 종료 시간을 정렬 + start_times.sort() + end_times.sort() + + # 회의실 개수 초기화 + rooms = 0 + end_index = 0 + + # 각 회의 시작 시간을 순회하며 회의실 개수 계산 + for start in start_times: + # 현재 회의 시작 시간이 이전 회의 종료 시간보다 크거나 같으면 회의실 개수 줄이기 + if start >= end_times[end_index]: + rooms -= 1 + end_index += 1 + rooms += 1 + + return rooms + + + +""" +힙 풀이 + +TC: O(N log N), 회의 시간 정렬 O(N log N) +SC: O(N), 최악의 경우 모든 회의가 겹침 +""" + +import heapq + +class Solution: + def minMeetingRooms(self, intervals: List[Interval]) -> int: + if not intervals: + return 0 + + intervals.sort(key=lambda x: x.start) + + heap = [] + + for interval in intervals: + start = interval.start + end = interval.end + + # 현재 회의의 시작 시간 >= 가장 빨리 끝나는 회의의 종료 시간이라면 + # 그 회의실은 다시 사용할 수 있으므로 heap에서 제거 (pop) + if heap and heap[0] <= start: + heapq.heappop(heap) + + # 새 회의의 종료 시간을 힙에 추가 + heapq.heappush(heap, end) + + # 힙에 남아 있는 종료 시간 수 = 동시에 진행 중인 회의 수 = 필요한 최소 회의실 수 + return len(heap)