feat: refactored docs structure

This commit is contained in:
Prad Nukala committed 2026-10-05 13:53:14 -04:00
1 parent d388b3000e
commit 9de25fecff
212 files changed
+641 -4169

No files matched your search

@@ -0,0 +1,72 @@
---
title: '127. Word Ladder'
description: 'A transformation sequence from word beginWord to word endWord using a dictionary wordList is a sequence of words beginWord -> s1 -> s2 -> ... -> sk such that:'
icon: dot
topics:
- { name: "Hash Table", slug: "hash-table" }
- { name: "String", slug: "string" }
- { name: "Breadth-First Search", slug: "breadth-first-search" }
- { name: "Bidirectional Search", slug: "bidirectional-search" }
issue: "https://github.com/prdlk/leetcode/issues/138"
sidebar:
label: 'Word Ladder'
badge: 'Hard'
---
### Example 1:
- Input: `beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log","cog"]`
- Output: `5`
- Explanation: One shortest transformation sequence is "hit" -> "hot" -> "dot" -> "dog" -> cog", which is `5` words long.
### Example 2:
- Input: `beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log"]`
- Output: `0`
- Explanation: The `endWord` "cog" is not in `wordList`, therefore there is no valid transformation sequence.
### Constraints:
- `1 <= beginWord.length <= 10`
- `endWord.length == beginWord.length`
- `1 <= wordList.length <= 5000`
- `wordList[i].length == beginWord.length`
- `beginWord`, `endWord`, and `wordList[i]` consist of lowercase English letters.
- `beginWord != endWord`
- All the words in `wordList` are unique.
## Solution
```py
import collections
class Solution:
def ladderLength(self, beginWord: str, endWord: str, wordList: list[str]) -> int:
if endWord not in wordList:
return 0
adj = collections.defaultdict(list)
wordList.append(beginWord)
for word in wordList:
for j in range(len(word)):
pattern = word[:j] + "*" + word[j + 1 :]
adj[pattern].append(word)
visit = set([beginWord])
q = collections.deque([beginWord])
res = 1
while q:
for i in range(len(q)):
word = q.popleft()
if word == endWord:
return res
for j in range(len(word)):
pattern = word[:j] + "*" + word[j + 1 :]
for adjWord in adj[pattern]:
if adjWord not in visit:
visit.add(adjWord)
q.append(adjWord)
res += 1
return 0
```
@@ -0,0 +1,76 @@
---
title: '994. Rotting Oranges'
description: 'You are given an m x n grid where each cell can have one of three values:'
icon: dot
topics:
- { name: "Array", slug: "array" }
- { name: "Breadth-First Search", slug: "breadth-first-search" }
- { name: "Matrix", slug: "matrix" }
issue: "https://github.com/prdlk/leetcode/issues/137"
sidebar:
label: 'Rotting Oranges'
badge: 'Medium'
---
### Example 1:
- Input: `grid = [[2,1,1],[1,1,0],[0,1,1]]`
- Output: `4`
### Example 2:
- Input: `grid = [[2,1,1],[0,1,1],[1,0,1]]`
- Output: `-1`
- Explanation: The orange in the bottom left corner (row `2`, column `0`) is never rotten, because rotting only happens 4-directionally.
### Example 3:
- Input: `grid = [[0,2]]`
- Output: `0`
- Explanation: Since there are already no fresh oranges at minute `0`, the answer is just `0`.
### Constraints:
- `m == grid.length`
- `n == grid[i].length`
- `1 <= m, n <= 10`
- grid[i][j] is 0, 1, or 2.
## Solution
```py
from collections import deque
class Solution:
def orangesRotting(self, grid: list[list[int]]) -> int:
q = deque()
time, fresh = 0, 0
rows, cols = len(grid), len(grid[0])
for r in range(rows):
for c in range(cols):
if grid[r][c] == 1:
fresh += 1
if grid[r][c] == 2:
q.append([r, c])
directions = [[0, 1], [0, -1], [1, 0], [-1, 0]]
while q and fresh > 0:
for i in range(len(q)):
r, c = q.popleft()
for dr, dc in directions:
row, col = dr + r, dc + c
# check if in bounds
if (
row < 0
or col < 0
or row == len(grid)
or col == len(grid[0])
or grid[row][col] != 1
):
continue
grid[row][col] = 2
q.append([row, col])
fresh -= 1
time += 1
return time if fresh == 0 else -1
```
+5
View File
@@ -0,0 +1,5 @@
---
title: Graph BFS
description: 'Nodes are visited in order of distance from the start, which gives the shortest path in an unweighted graph.'
hidden: true
---
@@ -0,0 +1,69 @@
---
title: '133. Clone Graph'
description: Given a reference of a node in a connected undirected graph
icon: dot
topics:
- { name: "Hash Table", slug: "hash-table" }
- { name: "Depth-First Search", slug: "depth-first-search" }
- { name: "Breadth-First Search", slug: "breadth-first-search" }
- { name: "Graph Theory", slug: "graph" }
issue: "https://github.com/prdlk/leetcode/issues/131"
sidebar:
label: 'Clone Graph'
badge: 'Medium'
---
### Example 1:
- Input: `adjList = [[2,4],[1,3],[2,4],[1,3]]`
- Output: `[[2,4],[1,3],[2,4],[1,3]]`
- Explanation: There are `4` nodes in the graph. 1st `node` (val = 1)'s neighbors are 2nd `node` (val = `2`) and 4th `node` (val = `4`). 2nd `node` (val = 2)'s neighbors are 1st `node` (val = `1`) and 3rd `node` (val = `3`). 3rd `node` (val = 3)'s neighbors are 2nd `node` (val = `2`) and 4th `node` (val = `4`). 4th `node` (val = 4)'s neighbors are 1st `node` (val = `1`) and 3rd `node` (val = `3`).
### Example 2:
- Input: `adjList = [[]]`
- Output: `[[]]`
- Explanation: Note that the input contains one empty list. The graph consists of only one `node` with val = `1` and it does not have any neighbors.
### Example 3:
- Input: `adjList = []`
- Output: `[]`
- Explanation: This an empty graph, it does not have any nodes.
### Constraints:
- The number of nodes in the graph is in the range [0, 100].
- `1 <= Node.val <= 100`
- Node.val is unique for each node.
- There are no repeated edges and no self-loops in the graph.
- The Graph is connected and all nodes can be visited starting from the given node.
## Solution
```py
"""
# Definition for a Node.
class Node:
def __init__(self, val = 0, neighbors = None):
self.val = val
self.neighbors = neighbors if neighbors is not None else []
"""
from typing import Optional
class Solution:
def cloneGraph(self, node: Optional["Node"]) -> Optional["Node"]:
oldToNew = {}
def dfs(node):
if node in oldToNew:
return oldToNew[node]
copy = Node(node.val)
oldToNew[node] = copy
for n in node.neighbors:
copy.neighbors.append(dfs(n))
return copy
return dfs(node) if node else None
```
@@ -0,0 +1,57 @@
---
title: '200. Number of Islands'
description: Given an m x n 2D binary grid grid which represents a map of '1's (land) and '0's (water), return the number of islands
icon: dot
topics:
- { name: "Array", slug: "array" }
- { name: "Depth-First Search", slug: "depth-first-search" }
- { name: "Breadth-First Search", slug: "breadth-first-search" }
- { name: "Union-Find", slug: "union-find" }
- { name: "Matrix", slug: "matrix" }
issue: "https://github.com/prdlk/leetcode/issues/130"
sidebar:
label: 'Number of Islands'
badge: 'Medium'
---
### Example 1:
- Input: `grid = [ ["1","1","1","1","0"], ["1","1","0","1","0"], ["1","1","0","0","0"], ["0","0","0","0","0"] ]`
- Output: `1`
### Example 2:
- Input: `grid = [ ["1","1","0","0","0"], ["1","1","0","0","0"], ["0","0","1","0","0"], ["0","0","0","1","1"] ]`
- Output: `3`
### Constraints:
- `m == grid.length`
- `n == grid[i].length`
- `1 <= m, n <= 300`
- grid[i][j] is '0' or '1'.
## Solution
```py
class Solution:
def numIslands(self, grid: List[List[str]]) -> int:
rows, cols = len(grid), len(grid[0])
def dfs(r, c):
if r < 0 or r >= rows or c < 0 or c >= cols or grid[r][c] != "1":
return
else:
grid[r][c] = "0"
dfs(r, c + 1) # right
dfs(r + 1, c) # bottom
dfs(r, c - 1) # left
dfs(r - 1, c) # top
num_islands = 0
for r in range(rows):
for c in range(cols):
if grid[r][c] == "1":
num_islands += 1
dfs(r, c)
return num_islands
```
@@ -0,0 +1,72 @@
---
title: '417. Pacific Atlantic Water Flow'
description: There is an m x n rectangular island that borders both the Pacific Ocean and Atlantic Ocean. The Pacific Ocean touches the island's left and top edges, and the Atlantic Ocean touches the island's right and bottom edges
icon: dot
topics:
- { name: "Array", slug: "array" }
- { name: "Depth-First Search", slug: "depth-first-search" }
- { name: "Breadth-First Search", slug: "breadth-first-search" }
- { name: "Matrix", slug: "matrix" }
issue: "https://github.com/prdlk/leetcode/issues/132"
sidebar:
label: 'Pacific Atlantic Water Flow'
badge: 'Medium'
---
### Example 1:
- Input: `heights = [[1,2,2,3,5],[3,2,3,4,4],[2,4,5,3,1],[6,7,1,4,5],[5,1,1,2,4]]`
- Output: `[[0,4],[1,3],[1,4],[2,2],[3,0],[3,1],[4,0]]`
- Explanation: The following cells can flow to the Pacific and Atlantic oceans, as shown below: `[0,4]`: `[0,4]` -> Pacific Ocean `[0,4]` -> Atlantic Ocean `[1,3]`: `[1,3]` -> `[0,3]` -> Pacific Ocean `[1,3]` -> `[1,4]` -> Atlantic Ocean `[1,4]`: `[1,4]` -> `[1,3]` -> `[0,3]` -> Pacific Ocean `[1,4]` -> Atlantic Ocean `[2,2]`: `[2,2]` -> `[1,2]` -> `[0,2]` -> Pacific Ocean `[2,2]` -> `[2,3]` -> `[2,4]` -> Atlantic Ocean `[3,0]`: `[3,0]` -> Pacific Ocean `[3,0]` -> `[4,0]` -> Atlantic Ocean `[3,1]`: `[3,1]` -> `[3,0]` -> Pacific Ocean `[3,1]` -> `[4,1]` -> Atlantic Ocean `[4,0]`: `[4,0]` -> Pacific Ocean `[4,0]` -> Atlantic Ocean Note that there are other possible paths for these cells to flow to the Pacific and Atlantic oceans.
### Example 2:
- Input: `heights = [[1]]`
- Output: `[[0,0]]`
- Explanation: The water can flow from the only cell to the Pacific and Atlantic oceans.
### Constraints:
- `m == heights.length`
- `n == heights[r].length`
- `1 <= m, n <= 200`
- `0 <= heights[r][c] <= 10^5`
## Solution
```py
class Solution:
def pacificAtlantic(self, heights: list[list[int]]) -> list[list[int]]:
rows, cols = len(heights), len(heights[0])
pac, atl = set(), set()
def dfs(r, c, seen, prevHeight):
if (
(r, c) in seen
or r < 0
or c < 0
or r == rows
or c == cols
or heights[r][c] < prevHeight
):
return
seen.add((r, c))
dfs(r + 1, c, seen, heights[r][c])
dfs(r - 1, c, seen, heights[r][c])
dfs(r, c + 1, seen, heights[r][c])
dfs(r, c - 1, seen, heights[r][c])
for c in range(cols):
dfs(0, c, pac, heights[0][c])
dfs(rows - 1, c, atl, heights[rows - 1][c])
for r in range(rows):
dfs(r, 0, pac, heights[r][0])
dfs(r, cols - 1, atl, heights[r][cols - 1])
res = []
for r in range(rows):
for c in range(cols):
if (r, c) in pac and (r, c) in atl:
res.append([r, c])
return res
```
+5
View File
@@ -0,0 +1,5 @@
---
title: Graph DFS
description: 'Each edge is followed to its end, and a visited set prevents a repeat visit.'
hidden: true
---
@@ -0,0 +1,62 @@
---
title: '207. Course Schedule'
description: There are a total of numCourses courses you have to take, labeled from 0 to numCourses - 1. You are given an array prerequisites where prerequisites[i] = [ai, bi] indicates that you must take course bi first if you want to take course ai
icon: dot
topics:
- { name: "Depth-First Search", slug: "depth-first-search" }
- { name: "Breadth-First Search", slug: "breadth-first-search" }
- { name: "Graph Theory", slug: "graph" }
- { name: "Topological Sort", slug: "topological-sort" }
- { name: "Directed Acyclic Graph", slug: "directed-acyclic-graph" }
issue: "https://github.com/prdlk/leetcode/issues/142"
sidebar:
label: 'Course Schedule'
badge: 'Medium'
---
### Example 1:
- Input: `numCourses = 2, prerequisites = [[1,0]]`
- Output: `true`
- Explanation: There are a total of `2` courses to take. To take course `1` you should have finished course `0`. So it is possible.
### Example 2:
- Input: `numCourses = 2, prerequisites = [[1,0],[0,1]]`
- Output: `false`
- Explanation: There are a total of `2` courses to take. To take course `1` you should have finished course `0`, and to take course `0` you should also have finished course `1`. So it is impossible.
### Constraints:
- `1 <= numCourses <= 2000`
- `0 <= prerequisites.length <= 5000`
- `prerequisites[i].length == 2`
- `0 <= ai, bi < numCourses`
- All the pairs `prerequisites[i]` are unique.
## Solution
```py
class Solution:
def canFinish(self, numCourses: int, prerequisites: list[list[int]]) -> bool:
indegree = [0] * numCourses
adj = [[] for x in range(numCourses)]
for prereq in prerequisites:
adj[prereq[1]].append(prereq[0])
indegree[prereq[0]] += 1
queue = []
for i in range(numCourses):
if indegree[i] == 0:
queue.append(i)
visited = 0
while queue:
node = queue.pop(0)
visited += 1
for neighbor in adj[node]:
indegree[neighbor] -= 1
if indegree[neighbor] == 0:
queue.append(neighbor)
return numCourses == visited
```
@@ -0,0 +1,76 @@
---
title: '210. Course Schedule II'
description: There are a total of numCourses courses you have to take, labeled from 0 to numCourses - 1. You are given an array prerequisites where prerequisites[i] = [ai, bi] indicates that you must take course bi first if you want to take course ai
icon: dot
topics:
- { name: "Depth-First Search", slug: "depth-first-search" }
- { name: "Breadth-First Search", slug: "breadth-first-search" }
- { name: "Graph Theory", slug: "graph" }
- { name: "Topological Sort", slug: "topological-sort" }
issue: "https://github.com/prdlk/leetcode/issues/143"
sidebar:
label: 'Course Schedule II'
badge: 'Medium'
---
### Example 1:
- Input: `numCourses = 2, prerequisites = [[1,0]]`
- Output: `[0,1]`
- Explanation: There are a total of `2` courses to take. To take course `1` you should have finished course `0`. So the correct course order is `[0,1]`.
### Example 2:
- Input: `numCourses = 4, prerequisites = [[1,0],[2,0],[3,1],[3,2]]`
- Output: `[0,2,1,3]`
- Explanation: There are a total of `4` courses to take. To take course `3` you should have finished both courses `1` and `2`. Both courses `1` and `2` should be taken after you finished course `0`. So one correct course order is `[0,1,2,3]`. Another correct ordering is `[0,2,1,3]`.
### Example 3:
- Input: `numCourses = 1, prerequisites = []`
- Output: `[0]`
### Constraints:
- `1 <= numCourses <= 2000`
- `0 <= prerequisites.length <= numCourses * (numCourses - 1)`
- `prerequisites[i].length == 2`
- `0 <= ai, bi < numCourses`
- `ai != bi`
- All the pairs [ai, bi] are distinct.
## Solution
```py
class Solution:
def findOrder(self, numCourses: int, prerequisites: list[list[int]]) -> list[int]:
prereq = {c: [] for c in range(numCourses)}
for crs, pre in prerequisites:
prereq[crs].append(pre)
# course has 3 possible states:
# visited -> crs has been added to output
# visiting -> crs not added to output, but added to cycle
# unvisited -> crs not added to output nor cycle
output = []
visit, cycle = set(), set()
def dfs(crs):
if crs in cycle:
return False
if crs in visit:
return True
cycle.add(crs)
for pre in prereq[crs]:
if dfs(pre) == False:
return False
cycle.remove(crs)
visit.add(crs)
output.append(crs)
return True
for c in range(numCourses):
if dfs(c) == False:
return []
return output
```
@@ -0,0 +1,92 @@
---
title: Topological Sort
description: 'Topological sort or topological ordering of a directed graph is an ordering of nodes such that every node appears in the ordering before all the nodes it points to. '
hidden: true
---
## Directed Graph
Before we get started on topological order, let's talk about directed graphs first. A graph is directed when its edges have directions (these edges are also called arcs). Suppose that v, w are vertices in a directed graph. Let's get familiar with a few concepts.
- **in-edge** - an edge pointing to or coming into the node
- **out-edge** - an edge pointing away or going out from the node (we will refer those nodes on the other end of the out-edges of v to v's neighbors)
- **in-degree** - the number of in-edges coming into the node
- **out-degree** - the number of out-edges going out from the node
- **dependency** - "v depends on w" / "v is dependent on w" means that there is a directed path from w to v where v, w are vertices in a directed graph. With these new terms under our belt, it will make more sense when we talk about directed graphs.
## What is a topological order?
Topological sort is not unique. For example, for a graph like this:
![Topological sort example](/topo_intro_1.png)
Both `[4, 5, 2]` and `[5, 4, 2]` are valid topological orders.
![Topological sort example](/topo_intro_2.png)
Task 3 is completely independent of task 2 and 4, and it can be anywhere in the order as long as it is before task 1 which depends on it. All of the following ordering are valid topological orderings.
`[4, 2, 3, 1], [4, 3, 2, 1], [3, 4, 2, 1]`
:::warning[Graphs with Cycles Do Not Have Topological Ordering]
It should be obvious that if a graph with a cycle does not have a topological ordering. In the above example, 2 has to come before 5 which has come before 4 which has to come before 2 which has to come before 5... which is impossible.
:::
## Kahn's Algorithm
To obtain a topological order, we can use Kahn's algorithm which is very similar to Breadth First Search.
In order to understand why we must use this algorithm we first look at the problem it is trying to solve.
Given a directed graph does there exist a way to remove the nodes such that each time we remove a node we guarantee that no other nodes point to that particular node?
For this algorithm, we systematically remove one node at a time, each time removing a node such that no other nodes point to that node (in-degree is 0). If no such node exists, then there must be a cycle, and there is no way to order the nodes such that "every node appears in the ordering before all the nodes it points to" (no solution). After removing the current node, for each neighboring node the current node points to, we check whether any nodes point to this node. If there isn't any, we push this node into the queue. Notice it is important to keep track of the number of nodes pointing to a node (in-degree) in the question as we only push the node into the queue once all nodes the current node depended on have been removed.
Here is a graphic to demonstrate the idea.
<Carousel>
<CarouselImage src='/topo_kahn.001.png' />
<CarouselImage src='/topo_kahn.002.png' />
<CarouselImage src='/topo_kahn.003.png' />
<CarouselImage src='/topo_kahn.004.png' />
<CarouselImage src='/topo_kahn.005.png' />
<CarouselImage src='/topo_kahn.006.png' />
<CarouselImage src='/topo_kahn.007.png' />
<CarouselImage src='/topo_kahn.009.png' />
<CarouselImage src='/topo_kahn.010.png' />
</Carousel>
## Example Code
:::tip
Notice the topological sort algorithm is very similar to BFS. The main difference is that we only push nodes with 0 in-degree into the queue in topological sort whereas in BFS we push all the neighboring nodes into the queue.
:::
```py
from collections import deque
def find_indegree(graph):
indegree = { node: 0 for node in graph } # dict
for node in graph:
for neighbor in graph[node]:
indegree[neighbor] += 1
return indegree
def topo_sort(graph):
res = []
q = deque()
indegree = find_indegree(graph)
for node in indegree:
if indegree[node] == 0:
q.append(node)
while len(q) > 0:
node = q.popleft()
res.append(node)
for neighbor in graph[node]:
indegree[neighbor] -= 1
if indegree[neighbor] == 0:
q.append(neighbor)
return res if len(graph) == len(res) else None
```
@@ -0,0 +1,63 @@
---
title: '323. Number of Connected Components in an Undirected Graph'
description: You have a graph of n nodes. You are given an integer n and an array edges where edges[i] = [ai, bi] indicates that there is an edge between ai and bi in the graph
icon: dot
topics:
- { name: "Depth-First Search", slug: "depth-first-search" }
- { name: "Breadth-First Search", slug: "breadth-first-search" }
- { name: "Union-Find", slug: "union-find" }
- { name: "Graph Theory", slug: "graph" }
issue: "https://github.com/prdlk/leetcode/issues/147"
sidebar:
label: 'Number of Connected Components in an Undirected Graph'
badge: 'Medium'
---
### Example 1:
- Input: `n = 5, edges = [[0,1],[1,2],[3,4]]`
- Output: `2`
### Example 2:
- Input: `n = 5, edges = [[0,1],[1,2],[2,3],[3,4]]`
- Output: `1`
### Constraints:
- `1 <= n <= 2000`
- `1 <= edges.length <= 5000`
- `edges[i]` = [ai, bi]
- `ai != bi`
- There are no repeated edges.
## Solution
```py
class Solution:
def countComponents(self, n: int, edges: list[list[int]]) -> int:
par = [i for i in range(n + 1)]
rank = [1] * (n + 1)
def find(n):
res = n
while res != par[res]:
par[res] = par[par[res]]
res = par[res]
return res
def union(n1, n2):
p1, p2 = find(n1), find(n2)
if p1 == p2:
return 0
if rank[p1] > rank[p2]:
par[p2] = p1
rank[p1] += rank[p2]
else:
par[p1] = p2
rank[p2] += rank[p1]
return 1
res = n
for n1, n2 in edges:
res -= union(n1, n2)
return res
```
@@ -0,0 +1,65 @@
---
title: '684. Redundant Connection'
description: In this problem, a tree is an undirected graph that is connected and has no cycles
icon: dot
topics:
- { name: "Depth-First Search", slug: "depth-first-search" }
- { name: "Breadth-First Search", slug: "breadth-first-search" }
- { name: "Union-Find", slug: "union-find" }
- { name: "Graph Theory", slug: "graph" }
issue: "https://github.com/prdlk/leetcode/issues/146"
sidebar:
label: 'Redundant Connection'
badge: 'Medium'
---
### Example 1:
- Input: `edges = [[1,2],[1,3],[2,3]]`
- Output: `[2,3]`
### Example 2:
- Input: `edges = [[1,2],[2,3],[3,4],[1,4],[1,5]]`
- Output: `[1,4]`
### Constraints:
- `n == edges.length`
- `3 <= n <= 1000`
- `edges[i].length == 2`
- `1 <= ai < bi <= edges.length`
- `ai != bi`
- There are no repeated edges.
- The given graph is connected.
## Solution
```py
class Solution:
def findRedundantConnection(self, edges: list[list[int]]) -> list[int]:
N = len(edges)
par = [i for i in range(N + 1)]
rank = [1] * (N + 1)
def find(n):
if n != par[n]:
par[n] = find(par[n])
return par[n]
def union(n1, n2):
p1, p2 = find(n1), find(n2)
if p1 == p2:
return False
if rank[p1] > rank[p2]:
par[p2] = p1
rank[p1] += rank[p2]
else:
par[p1] = p2
rank[p2] += rank[p1]
return True
for n1, n2 in edges:
if not union(n1, n2):
return [n1, n2]
```
@@ -0,0 +1,9 @@
---
title: Union Find
description: 'Union-Find (Disjoint Set Union) is a data structure that maintains a collection of disjoint sets and supports efficiently finding which set an element belongs to and merging two sets.'
hidden: true
---