Skip to content

2025/04/25 #12

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 5 commits into from
May 1, 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
76 changes: 76 additions & 0 deletions Hard/685 Redundant Connection II.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# 685. Redundant Connection II

## Intuition

The problem requires us to find a redundant edge in a directed graph that causes either a cycle or a node with two parents. The key insight is that there are two possible cases:

1. A node has two parents (in-degree > 1)
2. The graph contains a cycle

## Approach

1. First, we identify if there's a node with two parents by tracking the parent of each node. If found, we store the two candidate edges.
2. We use Union-Find (Disjoint Set Union) to detect cycles in the graph.
3. We process all edges except the second candidate edge (if it exists).
4. If we find a cycle:
- If we had a node with two parents, return the first candidate edge
- Otherwise, return the current edge that forms the cycle
5. If no cycle is found, return the second candidate edge (which must be the redundant one)

## Complexity

- Time complexity: O(n)
- Space complexity: O(n)

## Keywords

- Union-Find
- Graph
- Cycle Detection

## Code

```go
func findRedundantDirectedConnection(edges [][]int) []int {
parent, candidate1, candidate2 := make(map[int]int), -1, -1
for i, edge := range edges {
if ii, found := parent[edge[1]]; found {
candidate1, candidate2 = ii, i
break
}
parent[edge[1]] = i
}

root := make([]int, len(edges) + 1)
for i := range root {
root[i] = i
}

findRoot := func(n int) int {
for root[n] != n {
root[n] = root[root[n]]
n = root[n]
}
return n
}

for _, edge := range edges {
u, v := edge[0], edge[1]
if candidate2 != -1 && u == edges[candidate2][0] && v == edges[candidate2][1] {
continue
}

ur, uv := findRoot(u), findRoot(v)

if ur == uv {
if candidate1 != -1 {
return edges[candidate1]
}
return edge
}
root[uv] = ur
}

return edges[candidate2]
}
```
81 changes: 81 additions & 0 deletions Hard/847 Shortest Path Visiting All Nodes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# 847. Shortest Path Visiting All Nodes

## Intuition

The problem requires finding the shortest path that visits all nodes in an undirected graph. We can think of this as a state-based BFS problem where each state consists of:

1. The current node we're at
2. A bitmask representing which nodes we've visited so far

## Approach

1. Use BFS to explore all possible paths while keeping track of visited states
2. Each state is represented by a combination of:
- Current node position
- Bitmask indicating visited nodes (1 << i for node i)
3. Initialize the queue with all possible starting nodes
4. For each state, explore all neighboring nodes
5. Use a visited map to avoid revisiting the same state
6. The target is reached when all nodes are visited (bitmask equals 2^n - 1)

## Complexity

- Time complexity: O(n * 2^n)
- Space complexity: O(n * 2^n)

## Keywords

- BFS
- Bitmask
- State-based Search
- Graph Traversal
- Shortest Path

## Code

```go
func shortestPathLength(graph [][]int) int {
if len(graph) == 1 {
return 0
}

type unit struct {
n int
mask int
}
newUnit := func(i, m int) *unit {
return &unit{
n: i,
mask: m,
}
}

visited := make([]map[int]bool, len(graph))
queue := make([]*unit, 0)
for i := range graph {
mask := 1 << i
visited[i] = make(map[int]bool)
queue = append(queue, newUnit(i, mask))
}

target, ret := 1 << len(graph) - 1, 1
for {
n := len(queue)
for _, cur := range queue {
for _, nb := range graph[cur.n] {
nextMask := cur.mask | (1 << nb)
if !visited[nb][nextMask] {
if nextMask == target {
goto RETURN
}
visited[nb][nextMask], queue = true, append(queue, newUnit(nb, nextMask))
}
}
}
queue, ret = queue[n:], ret + 1
}

RETURN:
return ret
}
```
79 changes: 79 additions & 0 deletions Medium/210 Course Schedule II.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# 210. Course Schedule II

## Intuition

The problem is essentially about finding a valid topological order of courses given their prerequisites. We need to determine if it's possible to complete all courses and return the order in which they should be taken. This is a classic topological sorting problem where we need to find a linear ordering of vertices such that for every directed edge (u, v), vertex u comes before v in the ordering.

## Approach

1. Create a graph representation using an adjacency list and track the in-degree (number of prerequisites) for each course
2. Initialize a queue with courses that have no prerequisites (in-degree = 0)
3. Perform a BFS-like traversal:
- Remove a course from the queue and add it to the result
- For each course that has this course as a prerequisite, decrease their in-degree
- If a course's in-degree becomes 0, add it to the queue
4. If the length of the result equals the total number of courses, return the result; otherwise, return an empty array indicating it's impossible to complete all courses

## Complexity

- Time complexity: O(V + E)
- Space complexity: O(V + E)

## Keywords

- Topological Sort
- Graph
- BFS

## Code

```go
func findOrder(numCourses int, prerequisites [][]int) []int {
type node struct {
ingress int
used bool
neighbor []int
}

ret, record := make([]int, 0), make([]node, numCourses)

for _, pre := range prerequisites {
a, b := pre[0], pre[1]
if !record[b].used {
record[b].neighbor = make([]int, 0)
}
record[a].used, record[b].used = true, true
record[a].ingress, record[b].neighbor = record[a].ingress + 1, append(record[b].neighbor, a)
}

current := make([]int, 0)
for i, n := range record {
if !n.used {
ret = append(ret, i)
continue
}
if n.ingress == 0 {
current = append(current, i)
}
}

for len(current) != 0 {
ret = append(ret, current...)
n := len(current)
for _, num := range current {
for _, nb := range record[num].neighbor {
record[nb].ingress -= 1
if record[nb].ingress == 0 {
current = append(current, nb)
}
}
}
current = current[n:]
}

if len(ret) != numCourses {
return []int{}
}
return ret
}
```
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,18 @@
| 135 | Candy | [go](/Hard/135%20Candy.md) | H |
| 140 | Word Break II | [go](/Hard/140%20Word%20Break%20II.md) | H |
| 146 | LRU Cache | [go](/Medium/146%20LRU%20Cache.md) | M |
| 210 | Course Schedule II | [go](/Medium/210%20Course%20Schedule%20II.md) | M |
| 236 | Lowest Common Ancestor of a Binary Tree | [go](/Medium/236%20Lowest%20Common%20Ancestor%20of%20a%20Binary%20Tree.md) | M |
| 239 | Sliding Window Maximum | [go](/Hard/239%20Sliding%20Window%20Maximum.md) | H |
| 322 | Coin Change | [go](/Medium/322%20Coin%20Change.md) | M |
| 435 | Non-overlapping Interval | [go](/Medium/435%20Non-overlapping%20Intervals.md) | M |
| 460 | LFU Cache | [go](/Hard/460%20LFU%20Cache.md) | H |
| 592 | Fraction Addition and Subtraction | [go](/Medium/592%20Fraction%20Addition%20and%20Subtraction.md) | M |
| 685 | Redundant Connection II | [go](/Hard/685%20Redundant%20Connection%20II.md) | H |
| 752 | Open the Lock | [go](/Medium/752%20Open%20the%20Lock.md) | M |
| 757 | Set Intersection Size At Least Two | [go](/Hard/757%20Set%20Intersection%20Size%20At%20Least%20Two.md) | H |
| 815 | Bus Routes | [go](/Hard/815%20Bus%20Routes.md) | H |
| 847 | Shortest Path Visiting All Nodes | [go](/Hard/847%20Shortest%20Path%20Visiting%20All%20Nodes.md) | H |
| 765 | Couples Holding Hands | [go](/Hard/765%20Couples%20Holding%20Hands.md) | H |
| 834 | Sum of Distances in Tree | [go](/Hard/834%20Sum%20of%20Distances%20in%20Tree.md) | H |
| 840 | Magic Squares In Grid | [go](/Medium/840%20Magic%20Squares%20In%20Grid.md) | M |
Expand Down
Binary file added Week10/210 AC.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
79 changes: 79 additions & 0 deletions Week10/210 Course Schedule II.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# 210. Course Schedule II

## Intuition

The problem is essentially about finding a valid topological order of courses given their prerequisites. We need to determine if it's possible to complete all courses and return the order in which they should be taken. This is a classic topological sorting problem where we need to find a linear ordering of vertices such that for every directed edge (u, v), vertex u comes before v in the ordering.

## Approach

1. Create a graph representation using an adjacency list and track the in-degree (number of prerequisites) for each course
2. Initialize a queue with courses that have no prerequisites (in-degree = 0)
3. Perform a BFS-like traversal:
- Remove a course from the queue and add it to the result
- For each course that has this course as a prerequisite, decrease their in-degree
- If a course's in-degree becomes 0, add it to the queue
4. If the length of the result equals the total number of courses, return the result; otherwise, return an empty array indicating it's impossible to complete all courses

## Complexity

- Time complexity: O(V + E)
- Space complexity: O(V + E)

## Keywords

- Topological Sort
- Graph
- BFS

## Code

```go
func findOrder(numCourses int, prerequisites [][]int) []int {
type node struct {
ingress int
used bool
neighbor []int
}

ret, record := make([]int, 0), make([]node, numCourses)

for _, pre := range prerequisites {
a, b := pre[0], pre[1]
if !record[b].used {
record[b].neighbor = make([]int, 0)
}
record[a].used, record[b].used = true, true
record[a].ingress, record[b].neighbor = record[a].ingress + 1, append(record[b].neighbor, a)
}

current := make([]int, 0)
for i, n := range record {
if !n.used {
ret = append(ret, i)
continue
}
if n.ingress == 0 {
current = append(current, i)
}
}

for len(current) != 0 {
ret = append(ret, current...)
n := len(current)
for _, num := range current {
for _, nb := range record[num].neighbor {
record[nb].ingress -= 1
if record[nb].ingress == 0 {
current = append(current, nb)
}
}
}
current = current[n:]
}

if len(ret) != numCourses {
return []int{}
}
return ret
}
```
Binary file added Week10/685 AC.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading