Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SunaDu] Week 12 #1061

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 107 additions & 0 deletions number-of-connected-components-in-an-undirected-graph/dusunax.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
'''
# 323. Number of Connected Components in an Undirected Graph

- 무방향 그래프에서 연결된 컴포넌트(connected component)의 개수 구하기
- 노드 개수 n: 0 ~ n-1
- 간선 리스트 edges: 노드 쌍 [u, v]
- 연결된 컴포넌트의 개수를 반환한다.

## 그래프
- edges를 통해 인접 리스트(adjacency list)를 생성한다.
- 무방향 그래프이므로, u ↔ v 양쪽 방향으로 연결

## 풀이 알고리즘
- DFS(스택)/BFS(큐)
- 각 컴포넌트를 탐색하며 방문 처리
- 방문하지 않은 노드를 찾으면 새로운 컴포넌트이므로 카운트 증가
- Union-Find
- 각 노드가 어떤 그룹에 속하는 지 parent 배열로 표현
- find로 부모를 찾고, union으로 노드를 합친다.
- 최종적으로 독립된 그룹의 개수를 반환한다.
'''

'''
1. DFS(스택)
'''
def countComponentsDFS(self, n: int, edges: List[List[int]]) -> int:
graph = defaultdict(list)
for u, v in edges: # 인접 리스트 생성
graph[u].append(v)
graph[v].append(u)

visited = set() # 방문처리할 set
count = 0

def dfs(start):
stack = [start]
visited.add(start)

while stack:
curr = stack.pop()
for neighbor in graph[curr]:
if neighbor not in visited:
visited.add(neighbor)
stack.append(neighbor)

for node in range(n):
if node not in visited:
dfs(node)
count += 1

return count

'''
2. BFS(큐)
'''
def countComponentsBFS(self, n: int, edges: List[List[int]]) -> int:
graph = defaultdict(list)
for u, v in edges: # 인접 리스트 생성
graph[u].append(v)
graph[v].append(u)

visited = set() # 방문처리할 set
count = 0

def bfs(start):
queue = deque([start])
visited.add(start)

while queue:
curr = queue.popleft()
for neighbor in graph[curr]:
if neighbor not in visited:
visited.add(neighbor)
queue.append(neighbor)

for node in range(n):
if node not in visited:
bfs(node)
count += 1

return count

'''
3. Union-Find
- 간선을 순회하며 두 노드를 union으로 합친다.(parent 업데이트)
- find는 경로 압축을 통해 대표(root)를 찾는다.(parent에서 재귀로 찾기)
- 최종 root를 조사하고, 서로 다른 root의 개수를 반환한다.
'''
def countComponentsUnionFind(self, n: int, edges: List[List[int]]) -> int:
parent = list(range(n)) # 각 노드의 부모를 자신으로 초기화

def find(x):
if parent[x] != x:
parent[x] = find(parent[x]) # 경로 압축 path compression
return parent[x]

def union(x, y):
rootX = find(x)
rootY = find(y)
if rootX != rootY:
parent[rootX] = rootY

for u, v in edges:
union(u, v)

# 모든 노드의 최종 부모의, 유일한 root의 개수를 반환
return len(set(find(i) for i in range(n)))
70 changes: 70 additions & 0 deletions remove-nth-node-from-end-of-list/dusunax.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
'''
# 19. Remove Nth Node From End of List

## 풀이 접근방식 레퍼런스
- https://www.algodale.com/problems/remove-nth-node-from-end-of-list/
'''
class Solution:
def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
'''
# 1. 노드 리스트
- 음수 인덱스 원소 접근
- nodes 배열을 사용하여 마지막 n번째 노드를 찾는다.
- 메모리 낭비
TC: O(N), SC: O(N)
'''
# nodes = [] # 배열
# temp = ListNode(None, head)
# node = temp

# while node:
# nodes.append(node)
# node = node.next

# nodes[-n - 1].next = nodes[-n - 1].next.next

# return temp.next

'''
# 2. 큐
- 큐를 사용하여 마지막 n번째 노드를 찾는다.
- 최대 n+1개의 노드(필요한 범위)만 유지, 슬라이딩 윈도우
TC: O(N), SC: O(N)
'''
# queue = deque() #큐
# temp = ListNode(None, head)
# node = temp

# for _ in range(n + 1):
# queue.append(node)
# node = node.next

# while node:
# queue.popleft()
# queue.append(node)
# node = node.next

# queue[0].next = queue[0].next.next
# return temp.next

'''
# 3. 포인터
- 연결 리스트의 특성 이용
- 첫번째 포인터는 n번 이동하여 마지막 노드를 찾는다.
- 두번째 포인터는 첫번째 포인터가 마지막 노드를 찾을 때까지 이동한다.
- 두번째 포인터는 첫번째 포인터가 마지막 노드를 찾으면 첫번째 포인터의 이전 노드를 삭제한다.
TC: O(N), SC: O(1)
'''
first = head
for _ in range(n):
first = first.next

temp = ListNode(None, head)
second = temp

while first:
first = first.next
second = second.next

second.next = second.next.next
return temp.next
37 changes: 37 additions & 0 deletions same-tree/dusunax.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
'''
# 100. Same Tree

## Base case
- if both nodes are None, return True
- if one of the nodes is None, return False
- if the values of the nodes are different, return False

## Recursive case
- check if the left subtrees are the same
- check if the right subtrees are the same

## Time and Space Complexity

```
TC: O(n)
SC: O(n)
```

#### TC is O(n):
- visiting each node once, balanced or skewed, it's O(n)

#### SC is O(n):
- for h is tree's height.
- in best case(balanced): h is logN, so SC is O(logN)
- in worst case(skewed): h is N, so SC is O(N)
'''
class Solution:
def isSameTree(self, p: Optional[TreeNode], q: Optional[TreeNode]) -> bool:
if not p and not q:
return True
if not p or not q:
return False
if p.val != q.val:
return False

return self.isSameTree(p.left, q.left) and self.isSameTree(p.right, q.right)