Skip to content

Commit fb56f25

Browse files
chore: add LeetCode daily solution
1 parent 20a810b commit fb56f25

5 files changed

Lines changed: 177 additions & 0 deletions

File tree

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Check if There is a Valid Path in a Grid (Medium)
2+
3+
**Problem ID:** 1391
4+
**Date:** 2026-04-27
5+
**Link:** https://leetcode.com/problems/check-if-there-is-a-valid-path-in-a-grid/
6+
7+
## Approach
8+
9+
To solve the problem of checking if there is a valid path in the grid, we can utilize a graph traversal approach, specifically Depth-First Search (DFS) or Breadth-First Search (BFS). The main idea is to explore the grid starting from the upper-left cell (0, 0) and determine if we can reach the bottom-right cell (m - 1, n - 1) by following the connections defined by the street types in each cell.
10+
11+
### Approach:
12+
13+
1. **Understanding Connections**: Each cell in the grid has a specific type (from 1 to 6), which defines how it connects to its neighboring cells:
14+
- Type 1 connects left and right.
15+
- Type 2 connects up and down.
16+
- Type 3 connects left and down.
17+
- Type 4 connects right and down.
18+
- Type 5 connects left and up.
19+
- Type 6 connects right and up.
20+
21+
We need to define the valid movements based on these types. For example, if we are at cell (i, j) with type 1, we can move to (i, j-1) or (i, j+1) if those indices are valid.
22+
23+
2. **Traversal Strategy**: We can use either DFS or BFS to explore the grid. We maintain a set or a boolean matrix to keep track of visited cells to avoid cycles and unnecessary revisits.
24+
25+
3. **Starting Point**: Begin the traversal from the cell (0, 0). Check the possible movements based on the street type and move to the adjacent cells if they are valid and not visited.
26+
27+
4. **Termination Condition**: The traversal continues until we either reach the target cell (m - 1, n - 1) or exhaust all possible paths. If we reach the target cell, we return `true`. If we finish exploring without reaching the target, we return `false`.
28+
29+
### Data Structures:
30+
- A stack (for DFS) or a queue (for BFS) to manage the cells to be explored.
31+
- A set or a 2D boolean array to track visited cells.
32+
33+
### Complexity:
34+
- **Time Complexity**: O(m * n), where m is the number of rows and n is the number of columns in the grid. In the worst case, we may visit every cell once.
35+
- **Space Complexity**: O(m * n) for the visited structure and the stack/queue used for traversal.
36+
37+
This approach efficiently checks for a valid path by leveraging the connections defined by the street types, ensuring that all potential routes are explored systematically.
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import java.util.LinkedList;
2+
import java.util.Queue;
3+
4+
public class Solution {
5+
public boolean hasValidPath(int[][] grid) {
6+
int m = grid.length, n = grid[0].length;
7+
boolean[][] visited = new boolean[m][n];
8+
Queue<int[]> queue = new LinkedList<>();
9+
queue.offer(new int[]{0, 0});
10+
visited[0][0] = true;
11+
12+
while (!queue.isEmpty()) {
13+
int[] cell = queue.poll();
14+
int x = cell[0], y = cell[1];
15+
16+
if (x == m - 1 && y == n - 1) {
17+
return true;
18+
}
19+
20+
for (int[] direction : getDirections(grid[x][y])) {
21+
int newX = x + direction[0], newY = y + direction[1];
22+
if (isValid(newX, newY, m, n, visited, grid)) {
23+
visited[newX][newY] = true;
24+
queue.offer(new int[]{newX, newY});
25+
}
26+
}
27+
}
28+
return false;
29+
}
30+
31+
private boolean isValid(int x, int y, int m, int n, boolean[][] visited, int[][] grid) {
32+
return x >= 0 && x < m && y >= 0 && y < n && !visited[x][y] && canConnect(grid, x, y);
33+
}
34+
35+
private boolean canConnect(int[][] grid, int x, int y) {
36+
int cellType = grid[x][y];
37+
if (cellType == 1) return true; // left-right
38+
if (cellType == 2) return true; // up-down
39+
if (cellType == 3) return x > 0 && grid[x - 1][y] == 2; // left-down
40+
if (cellType == 4) return x < grid.length - 1 && grid[x + 1][y] == 2; // right-down
41+
if (cellType == 5) return y > 0 && grid[x][y - 1] == 1; // left-up
42+
if (cellType == 6) return y < grid[0].length - 1 && grid[x][y + 1] == 1; // right-up
43+
return false;
44+
}
45+
46+
private int[][] getDirections(int cellType) {
47+
switch (cellType) {
48+
case 1: return new int[][]{{0, 1}, {0, -1}}; // left-right
49+
case 2: return new int[][]{{1, 0}, {-1, 0}}; // up-down
50+
case 3: return new int[][]{{0, -1}, {1, 0}}; // left-down
51+
case 4: return new int[][]{{0, 1}, {1, 0}}; // right-down
52+
case 5: return new int[][]{{0, -1}, {-1, 0}}; // left-up
53+
case 6: return new int[][]{{0, 1}, {-1, 0}}; // right-up
54+
default: return new int[0][0];
55+
}
56+
}
57+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
var hasValidPath = function(grid) {
2+
const m = grid.length, n = grid[0].length;
3+
const directions = {
4+
1: [[0, 1], [0, -1]],
5+
2: [[1, 0], [-1, 0]],
6+
3: [[0, -1], [1, 0]],
7+
4: [[0, 1], [1, 0]],
8+
5: [[0, -1], [-1, 0]],
9+
6: [[0, 1], [-1, 0]]
10+
};
11+
12+
const canConnect = {
13+
1: [1, 1],
14+
2: [1, 1],
15+
3: [1, 0],
16+
4: [0, 1],
17+
5: [0, 1],
18+
6: [1, 0]
19+
};
20+
21+
const visited = Array.from({ length: m }, () => Array(n).fill(false));
22+
const queue = [[0, 0]];
23+
visited[0][0] = true;
24+
25+
while (queue.length) {
26+
const [x, y] = queue.shift();
27+
if (x === m - 1 && y === n - 1) return true;
28+
29+
const currentStreet = grid[x][y];
30+
for (const [dx, dy] of directions[currentStreet]) {
31+
const nx = x + dx;
32+
const ny = y + dy;
33+
if (nx < 0 || ny < 0 || nx >= m || ny >= n || visited[nx][ny]) continue;
34+
35+
const nextStreet = grid[nx][ny];
36+
if (canConnect[nextStreet][(dx === 0 ? 1 : 0)] && canConnect[nextStreet][(dy === 0 ? 1 : 0)]) {
37+
visited[nx][ny] = true;
38+
queue.push([nx, ny]);
39+
}
40+
}
41+
}
42+
43+
return false;
44+
};
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
class Solution:
2+
def hasValidPath(self, grid: List[List[int]]) -> bool:
3+
m, n = len(grid), len(grid[0])
4+
directions = {
5+
1: [(0, 1), (0, -1)],
6+
2: [(1, 0), (-1, 0)],
7+
3: [(0, 1), (1, 0)],
8+
4: [(0, 1), (-1, 0)],
9+
5: [(0, -1), (1, 0)],
10+
6: [(0, -1), (-1, 0)]
11+
}
12+
connect = {
13+
(0, 1): (0, -1),
14+
(0, -1): (0, 1),
15+
(1, 0): (-1, 0),
16+
(-1, 0): (1, 0)
17+
}
18+
19+
def is_valid(x, y):
20+
return 0 <= x < m and 0 <= y < n
21+
22+
visited = set()
23+
stack = [(0, 0)]
24+
25+
while stack:
26+
x, y = stack.pop()
27+
if (x, y) in visited:
28+
continue
29+
visited.add((x, y))
30+
if x == m - 1 and y == n - 1:
31+
return True
32+
33+
for dx, dy in directions[grid[x][y]]:
34+
nx, ny = x + dx, y + dy
35+
if is_valid(nx, ny) and (dx, dy) in connect and connect[(dx, dy)] in directions[grid[nx][ny]]:
36+
stack.append((nx, ny))
37+
38+
return False

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,3 +436,4 @@ Through completing the Blind 75 and NeetCode 150, you will have mastered:
436436
- 2026-04-24 — [Furthest Point From Origin](https://leetcode.com/problems/furthest-point-from-origin/) (Easy) → `Easy/2026-04-24-2833-Furthest-Point-From-Origin`
437437
- 2026-04-25 — [Maximize the Distance Between Points on a Square](https://leetcode.com/problems/maximize-the-distance-between-points-on-a-square/) (Hard) → `Hard/2026-04-25-3464-Maximize-the-Distance-Between-Points-on-a-Square`
438438
- 2026-04-26 — [Detect Cycles in 2D Grid](https://leetcode.com/problems/detect-cycles-in-2d-grid/) (Medium) → `Medium/2026-04-26-1559-Detect-Cycles-in-2D-Grid`
439+
- 2026-04-27 — [Check if There is a Valid Path in a Grid](https://leetcode.com/problems/check-if-there-is-a-valid-path-in-a-grid/) (Medium) → `Medium/2026-04-27-1391-Check-if-There-is-a-Valid-Path-in-a-Grid`

0 commit comments

Comments
 (0)