diff --git a/non-overlapping-intervals/hi-rachel.py b/non-overlapping-intervals/hi-rachel.py new file mode 100644 index 000000000..98d63050d --- /dev/null +++ b/non-overlapping-intervals/hi-rachel.py @@ -0,0 +1,36 @@ +""" +구간이 겹쳐 삭제해야 되는 최소 구간의 수를 반환해라 +-> Greedy +-> '겹치지 않게 최대한 많은 구간을 남기고, 나머지를 제거' + +문제 풀이 +1. 끝나는 시간 기준으로 오름차순 정렬 +2. 이전 구간의 끝과 현재 구간의 시작을 비교 +3. 겹치면 현재 구간을 제거 + +TC: O(n log n), 정렬 O(n log n) + 모든 interval 한 번씩 순회 O(n) +SC: O(1) +""" + +from typing import List + +class Solution: + def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int: + # 끝나는 시간이 빠른 순으로 정렬 + # -> 일찍 끝나는 구간을 선택하면, 이후에 더 많은 구간을 넣을 수 있는 여지가 커짐! + intervals.sort(key=lambda x: x[1]) + + # 첫 구간 선택 + prev_end = float('-inf') # 음의 무한대로 비교의 기준값 가장 작게 설정 -> 첫 번째 구간 무조건 선택됨 + count = 0 + + # 하나씩 검사 + for start, end in intervals: + if start >= prev_end: + # 겹치지 않음 -> 그대로 유지 + prev_end = end + else: + # 겹침 -> 하나 제거 + count += 1 + + return count diff --git a/number-of-connected-components-in-an-undirected-graph/hi-rachel.ts b/number-of-connected-components-in-an-undirected-graph/hi-rachel.ts new file mode 100644 index 000000000..65eceda04 --- /dev/null +++ b/number-of-connected-components-in-an-undirected-graph/hi-rachel.ts @@ -0,0 +1,70 @@ +/** + * TC: O(N + E), N: 노드의 개수, E: 간선의 개수 + * SC: O(N + E) + */ + +// DFS +function countComponentsDFS(n: number, edges: number[][]): number { + const graph: number[][] = Array.from({ length: n }, () => []); + + // 그래프 초기화 + for (const [u, v] of edges) { + graph[u].push(v); + graph[v].push(u); + } + + const visited = new Set(); + + function dfs(node: number) { + visited.add(node); + for (const neighbor of graph[node]) { + if (!visited.has(neighbor)) { + dfs(neighbor); + } + } + } + + let components = 0; + for (let i = 0; i < n; i++) { + if (!visited.has(i)) { + components++; + dfs(i); + } + } + + return components; +} + +// BFS +function countComponentsBFS(n: number, edges: number[][]): number { + const graph: number[][] = Array.from({ length: n }, () => []); + + // 그래프 초기화 + for (const [u, v] of edges) { + graph[u].push(v); + graph[v].push(u); + } + + const visited = new Set(); + let components = 0; + + for (let i = 0; i < n; i++) { + if (!visited.has(i)) { + components++; + const queue: number[] = [i]; + + while (queue.length > 0) { + const node = queue.shift(); + if (node === undefined) continue; + if (visited.has(node)) continue; + visited.add(node); + + for (const neighbor of graph[node]) { + if (!visited.has(neighbor)) queue.push(neighbor); + } + } + } + } + + return components; +} diff --git a/remove-nth-node-from-end-of-list/hi-rachel.py b/remove-nth-node-from-end-of-list/hi-rachel.py new file mode 100644 index 000000000..ed3be3790 --- /dev/null +++ b/remove-nth-node-from-end-of-list/hi-rachel.py @@ -0,0 +1,36 @@ +""" +뒤에서 n번째 노드를 없애고, linked list head를 반환해라 +TC: O(N), 리스트 한 번 순회 +SC: O(1), 포인터 2개만 사용 +""" + +from typing import Optional + +# Definition for singly-linked list. +class ListNode: + def __init__(self, val=0, next=None): + self.val = val + self.next = next + + +class Solution: + def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]: + # dummy node를 head 앞에 두어 edge case(head 삭제 등) 처리 + dummy = ListNode(0, head) + first = dummy + second = dummy + + # first를 n+1칸 먼저 이동 -> 두 포인터 사이 간격이 n + for _ in range(n + 1): + first = first.next + + # first가 끝에 도달할 때까지 두 포인터 함께 전진 + while first: + first = first.next + second = second.next + + # second의 다음 노드가 삭제 대상이므로 연결 건너뛰기 + second.next = second.next.next + + # 실제 head는 dummy.next에 있음 + return dummy.next diff --git a/same-tree/hi-rachel.py b/same-tree/hi-rachel.py new file mode 100644 index 000000000..09538aec4 --- /dev/null +++ b/same-tree/hi-rachel.py @@ -0,0 +1,50 @@ +from typing import Optional + +# 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 + +""" +재귀 풀아 +TC: O(N), 양 트리의 각 노드를 상대로 재귀함수를 딱 1번씩만 호출하기 때문 +SC: O(N), 공간복잡도 = 콜스택의 최대 높이(노드의 수) +""" +class Solution: + def isSameTree(self, p: Optional[TreeNode], q: Optional[TreeNode]) -> bool: + # 둘 다 null이면 True 반환 + if not p and not q: + return True + # 둘 중 하나면 null이면 False 반환 + if not p or not q: + return False + # val이 서로 다르면 탐색 중단, False 반환 + if p.val != q.val: + return False + # 현재 node의 val이 같다면, 좌우측 자식 트리도 같은지 확인 -> 재귀 호출 + return self.isSameTree(p.left, q.left) and self.isSameTree(p.right, q.right) + + +""" +스택 풀이 +TC: O(N) +SC: O(N) +""" + +class Solution: + def isSameTree(self, p: Optional[TreeNode], q: Optional[TreeNode]) -> bool: + stack = [(p, q)] + + while stack: + p, q = stack.pop() + if not p and not q: + continue + if not p or not q: + return False + if p.val != q.val: + return False + stack.append((p.left, q.left)) + stack.append((p.right, q.right)) + return True