Skip to content

Commit af4b350

Browse files
authored
Merge pull request #1061 from dusunax/main
[SunaDu] Week 12
2 parents 72fd840 + 6079c79 commit af4b350

File tree

4 files changed

+267
-0
lines changed

4 files changed

+267
-0
lines changed

non-overlapping-intervals/dusunax.py

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
'''
2+
# 435. Non-overlapping Intervals
3+
4+
## understanding the problem
5+
- 겹치지 않는 인터벌을 최대한 많이 남기기 위해, 지워야하는 인터벌의 최소값 반환
6+
- not it: 그래프에 순환이 있는지 여부를 알아본다❌ overkill
7+
- core approach: Greedy
8+
9+
## Greedy
10+
- Whenever you detect an overlap, you should remove one.
11+
- not physically. we'll going to return counts, so just keep counts is enough.
12+
- Goal: keep many non-overlapping intervals as possible to minimize removal.
13+
14+
### how to detect overlap?
15+
1. sort intervals by ends
16+
2. iterate through intervals while tracking down the prev_end(last vaild end time)
17+
- if `current_start < prev_end`, it's overlap.
18+
- Example:
19+
- [2, 4] is overlap with [1, 3]
20+
- start (2) is smaller than end (3)
21+
22+
### which one should removed?
23+
- when overlap happens, remove the interval that ends later
24+
- it will restrict more future intervals.
25+
- the longer an interval lasts, the more it blocks others(leaving less room for non-overlapping intervals)
26+
27+
## complexity
28+
29+
### TC is O(n log n)
30+
- sorting: O(n log n)
31+
- iterating: O(n)
32+
- total: O(n log n)
33+
34+
### SC is O(1)
35+
- no extra space is used
36+
'''
37+
class Solution:
38+
def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int:
39+
intervals.sort(key=lambda x: x[1])
40+
41+
count = 0 # number of removals
42+
prev_end = intervals[0][1] # last valid end time
43+
44+
for start, end in intervals[1:]: # 1 ~ n-1
45+
if start < prev_end: # overlap detected
46+
count += 1
47+
# <do NOT move the prev_end pointer>
48+
# prev_end is still pointing at the previous interval
49+
# so it's keeping the interval ends earlier. (removing the longer one)
50+
else:
51+
prev_end = end
52+
53+
return count
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
'''
2+
# 323. Number of Connected Components in an Undirected Graph
3+
4+
- 무방향 그래프에서 연결된 컴포넌트(connected component)의 개수 구하기
5+
- 노드 개수 n: 0 ~ n-1
6+
- 간선 리스트 edges: 노드 쌍 [u, v]
7+
- 연결된 컴포넌트의 개수를 반환한다.
8+
9+
## 그래프
10+
- edges를 통해 인접 리스트(adjacency list)를 생성한다.
11+
- 무방향 그래프이므로, u ↔ v 양쪽 방향으로 연결
12+
13+
## 풀이 알고리즘
14+
- DFS(스택)/BFS(큐)
15+
- 각 컴포넌트를 탐색하며 방문 처리
16+
- 방문하지 않은 노드를 찾으면 새로운 컴포넌트이므로 카운트 증가
17+
- Union-Find
18+
- 각 노드가 어떤 그룹에 속하는 지 parent 배열로 표현
19+
- find로 부모를 찾고, union으로 노드를 합친다.
20+
- 최종적으로 독립된 그룹의 개수를 반환한다.
21+
'''
22+
23+
'''
24+
1. DFS(스택)
25+
'''
26+
def countComponentsDFS(self, n: int, edges: List[List[int]]) -> int:
27+
graph = defaultdict(list)
28+
for u, v in edges: # 인접 리스트 생성
29+
graph[u].append(v)
30+
graph[v].append(u)
31+
32+
visited = set() # 방문처리할 set
33+
count = 0
34+
35+
def dfs(start):
36+
stack = [start]
37+
visited.add(start)
38+
39+
while stack:
40+
curr = stack.pop()
41+
for neighbor in graph[curr]:
42+
if neighbor not in visited:
43+
visited.add(neighbor)
44+
stack.append(neighbor)
45+
46+
for node in range(n):
47+
if node not in visited:
48+
dfs(node)
49+
count += 1
50+
51+
return count
52+
53+
'''
54+
2. BFS(큐)
55+
'''
56+
def countComponentsBFS(self, n: int, edges: List[List[int]]) -> int:
57+
graph = defaultdict(list)
58+
for u, v in edges: # 인접 리스트 생성
59+
graph[u].append(v)
60+
graph[v].append(u)
61+
62+
visited = set() # 방문처리할 set
63+
count = 0
64+
65+
def bfs(start):
66+
queue = deque([start])
67+
visited.add(start)
68+
69+
while queue:
70+
curr = queue.popleft()
71+
for neighbor in graph[curr]:
72+
if neighbor not in visited:
73+
visited.add(neighbor)
74+
queue.append(neighbor)
75+
76+
for node in range(n):
77+
if node not in visited:
78+
bfs(node)
79+
count += 1
80+
81+
return count
82+
83+
'''
84+
3. Union-Find
85+
- 간선을 순회하며 두 노드를 union으로 합친다.(parent 업데이트)
86+
- find는 경로 압축을 통해 대표(root)를 찾는다.(parent에서 재귀로 찾기)
87+
- 최종 root를 조사하고, 서로 다른 root의 개수를 반환한다.
88+
'''
89+
def countComponentsUnionFind(self, n: int, edges: List[List[int]]) -> int:
90+
parent = list(range(n)) # 각 노드의 부모를 자신으로 초기화
91+
92+
def find(x):
93+
if parent[x] != x:
94+
parent[x] = find(parent[x]) # 경로 압축 path compression
95+
return parent[x]
96+
97+
def union(x, y):
98+
rootX = find(x)
99+
rootY = find(y)
100+
if rootX != rootY:
101+
parent[rootX] = rootY
102+
103+
for u, v in edges:
104+
union(u, v)
105+
106+
# 모든 노드의 최종 부모의, 유일한 root의 개수를 반환
107+
return len(set(find(i) for i in range(n)))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
'''
2+
# 19. Remove Nth Node From End of List
3+
4+
## 풀이 접근방식 레퍼런스
5+
- https://www.algodale.com/problems/remove-nth-node-from-end-of-list/
6+
'''
7+
class Solution:
8+
def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
9+
'''
10+
# 1. 노드 리스트
11+
- 음수 인덱스 원소 접근
12+
- nodes 배열을 사용하여 마지막 n번째 노드를 찾는다.
13+
- 메모리 낭비
14+
TC: O(N), SC: O(N)
15+
'''
16+
# nodes = [] # 배열
17+
# temp = ListNode(None, head)
18+
# node = temp
19+
20+
# while node:
21+
# nodes.append(node)
22+
# node = node.next
23+
24+
# nodes[-n - 1].next = nodes[-n - 1].next.next
25+
26+
# return temp.next
27+
28+
'''
29+
# 2. 큐
30+
- 큐를 사용하여 마지막 n번째 노드를 찾는다.
31+
- 최대 n+1개의 노드(필요한 범위)만 유지, 슬라이딩 윈도우
32+
TC: O(N), SC: O(N)
33+
'''
34+
# queue = deque() #큐
35+
# temp = ListNode(None, head)
36+
# node = temp
37+
38+
# for _ in range(n + 1):
39+
# queue.append(node)
40+
# node = node.next
41+
42+
# while node:
43+
# queue.popleft()
44+
# queue.append(node)
45+
# node = node.next
46+
47+
# queue[0].next = queue[0].next.next
48+
# return temp.next
49+
50+
'''
51+
# 3. 포인터
52+
- 연결 리스트의 특성 이용
53+
- 첫번째 포인터는 n번 이동하여 마지막 노드를 찾는다.
54+
- 두번째 포인터는 첫번째 포인터가 마지막 노드를 찾을 때까지 이동한다.
55+
- 두번째 포인터는 첫번째 포인터가 마지막 노드를 찾으면 첫번째 포인터의 이전 노드를 삭제한다.
56+
TC: O(N), SC: O(1)
57+
'''
58+
first = head
59+
for _ in range(n):
60+
first = first.next
61+
62+
temp = ListNode(None, head)
63+
second = temp
64+
65+
while first:
66+
first = first.next
67+
second = second.next
68+
69+
second.next = second.next.next
70+
return temp.next

same-tree/dusunax.py

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
'''
2+
# 100. Same Tree
3+
4+
## Base case
5+
- if both nodes are None, return True
6+
- if one of the nodes is None, return False
7+
- if the values of the nodes are different, return False
8+
9+
## Recursive case
10+
- check if the left subtrees are the same
11+
- check if the right subtrees are the same
12+
13+
## Time and Space Complexity
14+
15+
```
16+
TC: O(n)
17+
SC: O(n)
18+
```
19+
20+
#### TC is O(n):
21+
- visiting each node once, balanced or skewed, it's O(n)
22+
23+
#### SC is O(n):
24+
- for h is tree's height.
25+
- in best case(balanced): h is logN, so SC is O(logN)
26+
- in worst case(skewed): h is N, so SC is O(N)
27+
'''
28+
class Solution:
29+
def isSameTree(self, p: Optional[TreeNode], q: Optional[TreeNode]) -> bool:
30+
if not p and not q:
31+
return True
32+
if not p or not q:
33+
return False
34+
if p.val != q.val:
35+
return False
36+
37+
return self.isSameTree(p.left, q.left) and self.isSameTree(p.right, q.right)

0 commit comments

Comments
 (0)