Skip to content

[SunaDu] Week 11 #1034

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

Merged
merged 4 commits into from
Feb 22, 2025
Merged
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
67 changes: 67 additions & 0 deletions graph-valid-tree/dusunax.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
'''
# 261. Graph Valid Tree

## What constitutes a 🌲
1. it's a graph.
2. Connected: edges == n - 1, visited node count == n
3. Acyclic: there is no cycle.

## Approach A. DFS
use DFS to check if there is a cycle in the graph.
- if there were no cycle & visited node count == n, return True.

## Approach B. Disjoint Set Union (서로소 집합)
use Disjoint Set Union to check if there is a cycle in the graph.
- if you find a cycle, return False immediately.
- if you find no cycle, return True.

### Union Find Operation
- Find: find the root of a node.
- if the root of two nodes is already the same, there is a cycle.
- Union: connect two nodes.

## Approach Comparison
- **A. DFS**: simple and easy to understand.
- **B. Disjoint Set Union**: quicker to check if there is a cycle. if there were more edges, Union Find would be faster.
'''
class Solution:
def validTreeDFS(self, n: int, edges: List[List[int]]) -> bool:
if len(edges) != n - 1:
return False

graph = [[] for _ in range(n)]
for node, neighbor in edges:
graph[node].append(neighbor)
graph[neighbor].append(node)

visited = set()
def dfs(node):
visited.add(node)
for neighbor in graph[node]:
if neighbor not in visited:
dfs(neighbor)

dfs(0)
return len(visited) == n

def validTreeUnionFind(self, n: int, edges: List[List[int]]) -> bool:
if len(edges) != n - 1:
return False

parent = [i for i in range(n)]

def find(x):
if x == parent[x]:
return x
parent[x] = find(parent[x])
return parent[x]

def union(x, y):
parent[find(x)] = find(y)

for node, neighbor in edges:
if find(node) == find(neighbor):
return False
union(node, neighbor)

return True
61 changes: 61 additions & 0 deletions maximum-depth-of-binary-tree/dusunax.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
'''
# 104. Maximum Depth of Binary Tree

use DFS to find the maximum depth of the binary tree.

## A. if we use a helper function (not a good idea)
```
helper function explanation:
- store the max_depth in the nonlocal variable.(outside of the helper function)
- base case: if the node is None, update the max_depth and return.
- in the helper function, do recursive call for the left and right children of the node.
- update the count for the depth of the tree.
- update the max_depth when the node is a leaf node's children.
```

## B. return the max_depth directly
👉 why helper function is not necessary?
recursion function can return the max_depth directly.
remove side effect & non-local variable.

## TC is O(n)

visit each node once for checking if it is a leaf node's children.

## SC is O(h)

h for height of the tree
'''
class Solution:
'''
A. first approach with side effect
'''
def maxDepthWithHelper(self, root: Optional[TreeNode]) -> int:
max_depth = 0

def helper(node, count):
nonlocal max_depth
if node is None:
max_depth = max(max_depth, count)
return

helper(node.left, count+1)
helper(node.right, count + 1)

helper(root, max_depth)

return max_depth

'''
B. return the max_depth directly.
- more concise & readable
- no side effect & non-local variable
'''
def maxDepth(self, root: Optional[TreeNode]) -> int:
if root is None:
return 0

left_count = self.maxDepth(root.left)
right_count = self.maxDepth(root.right)

return max(left_count, right_count) + 1
49 changes: 49 additions & 0 deletions reorder-list/dusunax.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
'''
# 143. Reorder list
use two pointers for each steps.

1. finding the middle
- two pointers: slow, fast
- move slow 1, fast 2 until fast reaches the end.
2. reversing the second half
- two pointers: prev, curr
- start from slow to end, do common reverse linked list operation.
- need to break the links to halves beforehand.
3. merging first & second
- two pointers: frist, second
- merge second between first, until second is None

## TC is O(n)
- find the middle: O(n)
- reverse the second half: O(n)
- merge the two halves: O(n)

## SC is O(1)
- no extra space is used.
'''
class Solution:
def reorderList(self, head: Optional[ListNode]) -> None:
# 1. finding the middle
slow, fast = head, head
while fast and fast.next:
slow = slow.next
fast = fast.next.next

# 2. reversing second half
second_not_reversed = slow.next
slow.next = None
prev, curr = None, second_not_reversed
while curr:
temp = curr.next
curr.next = prev
prev = curr
curr = temp

# 3. merging first & second
first, second = head, prev
while second:
temp1, temp2 = first.next, second.next
first.next = second
second.next = temp1
first = temp1
second = temp2