π― PHP Graphs β Top 25 Interview Questions
Q1 β Number of Islands
π§ How to Approach This
Step 1 β Recognize the pattern: Count connected components in a grid; DFS/BFS sinks each island.
Step 2 β Brute force (what NOT to do): Check every cell repeatedly β O(RΒ²CΒ²).
Step 3 β Optimal insight: DFS from each unvisited '1'; mark all reachable cells as visited.
Step 4 β Algorithm:
1. Iterate all cells; if '1' β DFS to sink island, increment count
2. DFS: mark cell '0', recurse 4 directions
Step 5 β Edge cases: All water; all land; single cell.
function numIslands(array $grid): int {
$rows = count($grid); $cols = count($grid[0]); $count = 0;
$dfs = function(int $r, int $c) use (&$dfs, &$grid, $rows, $cols): void {
if ($r < 0 || $r >= $rows || $c < 0 || $c >= $cols || $grid[$r][$c] !== '1') return;
$grid[$r][$c] = '0';
$dfs($r+1,$c); $dfs($r-1,$c); $dfs($r,$c+1); $dfs($r,$c-1);
};
for ($r = 0; $r < $rows; $r++)
for ($c = 0; $c < $cols; $c++)
if ($grid[$r][$c] === '1') { $dfs($r, $c); $count++; }
return $count;
}
Time: O(R Γ C) | Space: O(R Γ C)
Q2 β Clone Graph
π§ How to Approach This
Step 1 β Recognize the pattern: BFS/DFS with a visited map storing [original β clone].
Step 2 β Brute force (what NOT to do): Rebuild without tracking clones β infinite loop on cycles.
Step 3 β Optimal insight: HashMap maps original node to its clone; create clone on first visit.
Step 4 β Algorithm:
1. BFS queue; on first visit create clone and add to map
2. For each neighbor: create if not seen; add to clone's neighbors
Step 5 β Edge cases: Null input; single node with no neighbors.
class Node { public int $val; public array $neighbors; public function __construct(int $v){ $this->val=$v; $this->neighbors=[]; } }
function cloneGraph(?Node $node): ?Node {
if ($node === null) return null;
$map = [];
$queue = [$node];
$map[$node->val] = new Node($node->val);
while (!empty($queue)) {
$curr = array_shift($queue);
foreach ($curr->neighbors as $neighbor) {
if (!isset($map[$neighbor->val])) {
$map[$neighbor->val] = new Node($neighbor->val);
$queue[] = $neighbor;
}
$map[$curr->val]->neighbors[] = $map[$neighbor->val];
}
}
return $map[$node->val];
}
Time: O(V + E) | Space: O(V)
Q3 β Course Schedule (Cycle Detection)
π§ How to Approach This
Step 1 β Recognize the pattern: Directed graph cycle detection β if cycle exists, impossible to finish.
Step 2 β Brute force (what NOT to do): Try all orderings β O(n!).
Step 3 β Optimal insight: Kahn's topological sort; if all nodes processed β no cycle.
Step 4 β Algorithm:
1. Build graph + indegree array
2. Enqueue all indegree-0 nodes
3. Process queue: decrement neighbors' indegree; enqueue when 0
4. If processed count === n β true
Step 5 β Edge cases: No prerequisites; self-loop.
function canFinish(int $n, array $prereqs): bool {
$graph = array_fill(0, $n, []);
$indeg = array_fill(0, $n, 0);
foreach ($prereqs as [$a, $b]) { $graph[$b][] = $a; $indeg[$a]++; }
$queue = array_keys(array_filter($indeg, fn($d) => $d === 0));
$count = 0;
while (!empty($queue)) {
$node = array_shift($queue); $count++;
foreach ($graph[$node] as $nb) { if (--$indeg[$nb] === 0) $queue[] = $nb; }
}
return $count === $n;
}
Time: O(V + E) | Space: O(V + E)
Q4 β Course Schedule II (Topological Order)
π§ How to Approach This
Step 1 β Recognize the pattern: Same as Q3 but return the topological ordering.
Step 2 β Brute force (what NOT to do): DFS without tracking β O(VΒ²).
Step 3 β Optimal insight: Kahn's BFS; collect order while processing.
Step 4 β Algorithm:
1. Same as Course Schedule I
2. Append each dequeued node to order
3. Return order if complete, else []
Step 5 β Edge cases: Multiple valid orderings exist.
function findOrder(int $n, array $prereqs): array {
$graph = array_fill(0, $n, []);
$indeg = array_fill(0, $n, 0);
foreach ($prereqs as [$a, $b]) { $graph[$b][] = $a; $indeg[$a]++; }
$queue = array_keys(array_filter($indeg, fn($d) => $d === 0));
$order = [];
while (!empty($queue)) {
$node = array_shift($queue); $order[] = $node;
foreach ($graph[$node] as $nb) { if (--$indeg[$nb] === 0) $queue[] = $nb; }
}
return count($order) === $n ? $order : [];
}
Time: O(V + E) | Space: O(V + E)
Q5 β Pacific Atlantic Water Flow
π§ How to Approach This
Step 1 β Recognize the pattern: Reverse flow β BFS from ocean borders inward; find cells reachable from both.
Step 2 β Brute force (what NOT to do): DFS from every cell to both oceans β O(RΒ²CΒ²).
Step 3 β Optimal insight: Multi-source BFS from Pacific border cells and Atlantic border cells separately.
Step 4 β Algorithm:
1. BFS from top+left (Pacific); BFS from bottom+right (Atlantic)
2. Return cells in both reachable sets
Step 5 β Edge cases: 1Γ1 grid β always both; uniform height grid.
function pacificAtlantic(array $heights): array {
$R = count($heights); $C = count($heights[0]);
$bfs = function(array $starts) use ($heights, $R, $C): array {
$visited = []; $queue = $starts;
foreach ($starts as [$r,$c]) $visited["$r,$c"] = true;
$dirs = [[1,0],[-1,0],[0,1],[0,-1]];
while (!empty($queue)) {
[$r,$c] = array_shift($queue);
foreach ($dirs as [$dr,$dc]) {
$nr=$r+$dr; $nc=$c+$dc;
if ($nr>=0&&$nr<$R&&$nc>=0&&$nc<$C&&!isset($visited["$nr,$nc"])
&&$heights[$nr][$nc]>=$heights[$r][$c]) {
$visited["$nr,$nc"]=true; $queue[]=[$nr,$nc];
}
}
}
return $visited;
};
$pac=[]; $atl=[];
for($r=0;$r<$R;$r++){ $pac[]=[$r,0]; $atl[]=[$r,$C-1]; }
for($c=0;$c<$C;$c++){ $pac[]=[0,$c]; $atl[]=[$R-1,$c]; }
$pv=$bfs($pac); $av=$bfs($atl);
$res=[];
for($r=0;$r<$R;$r++) for($c=0;$c<$C;$c++)
if(isset($pv["$r,$c"])&&isset($av["$r,$c"])) $res[]=[$r,$c];
return $res;
}
Time: O(R Γ C) | Space: O(R Γ C)
Q6 β Surrounded Regions
π§ How to Approach This
Step 1 β Recognize the pattern: Mark border-connected 'O' cells; flip remaining 'O' to 'X'.
Step 2 β Brute force (what NOT to do): Check every 'O' for border reachability β repeated traversal.
Step 3 β Optimal insight: DFS from all border 'O' cells β mark safe; then flip/restore.
Step 4 β Algorithm:
1. DFS from all border 'O' β mark as 'S'
2. Scan: 'O' β 'X', 'S' β 'O'
Step 5 β Edge cases: No 'O'; all border 'O'.
function solve(array &$board): void {
$R=count($board); $C=count($board[0]);
$dfs=function(int $r,int $c) use(&$dfs,&$board,$R,$C):void{
if($r<0||$r>=$R||$c<0||$c>=$C||$board[$r][$c]!=='O') return;
$board[$r][$c]='S';
$dfs($r+1,$c);$dfs($r-1,$c);$dfs($r,$c+1);$dfs($r,$c-1);
};
for($r=0;$r<$R;$r++){ $dfs($r,0); $dfs($r,$C-1); }
for($c=0;$c<$C;$c++){ $dfs(0,$c); $dfs($R-1,$c); }
for($r=0;$r<$R;$r++) for($c=0;$c<$C;$c++)
$board[$r][$c] = $board[$r][$c]==='S' ? 'O' : ($board[$r][$c]==='O' ? 'X' : $board[$r][$c]);
}
Time: O(R Γ C) | Space: O(R Γ C)
Q7 β Rotting Oranges
π§ How to Approach This
Step 1 β Recognize the pattern: Multi-source BFS from all rotten oranges simultaneously.
Step 2 β Brute force (what NOT to do): BFS from each rotten orange separately then take max β O(RΒ²CΒ²).
Step 3 β Optimal insight: Add all rotten oranges to queue at t=0; BFS spreads rot level by level.
Step 4 β Algorithm:
1. Enqueue all rotten (2), count fresh
2. BFS: each level = 1 minute; convert fresh neighbors to rotten, decrement fresh
3. Return minutes if fresh==0, else -1
Step 5 β Edge cases: No fresh oranges β 0; isolated fresh β -1.
function orangesRotting(array $grid): int {
$R=count($grid); $C=count($grid[0]); $fresh=0; $queue=[];
for($r=0;$r<$R;$r++) for($c=0;$c<$C;$c++){
if($grid[$r][$c]===2) $queue[]=[$r,$c,0];
if($grid[$r][$c]===1) $fresh++;
}
$dirs=[[1,0],[-1,0],[0,1],[0,-1]]; $mins=0;
while(!empty($queue)){
[$r,$c,$t]=array_shift($queue); $mins=max($mins,$t);
foreach($dirs as[$dr,$dc]){
$nr=$r+$dr;$nc=$c+$dc;
if($nr>=0&&$nr<$R&&$nc>=0&&$nc<$C&&$grid[$nr][$nc]===1){
$grid[$nr][$nc]=2; $fresh--; $queue[]=[$nr,$nc,$t+1];
}
}
}
return $fresh===0 ? $mins : -1;
}
Time: O(R Γ C) | Space: O(R Γ C)
Q8 β Walls and Gates
π§ How to Approach This
Step 1 β Recognize the pattern: Multi-source BFS from all gates (0); fill rooms with shortest distance.
Step 2 β Brute force (what NOT to do): BFS from each room to find nearest gate β O((RC)Β²).
Step 3 β Optimal insight: Multi-source BFS from all gates simultaneously; distance propagates outward.
Step 4 β Algorithm:
1. Enqueue all gates (0)
2. BFS: set neighbor's dist = curr dist + 1 if currently INF
Step 5 β Edge cases: No gates; no rooms.
function wallsAndGates(array &$rooms): void {
$INF=PHP_INT_MAX; $R=count($rooms); $C=count($rooms[0]); $queue=[];
for($r=0;$r<$R;$r++) for($c=0;$c<$C;$c++)
if($rooms[$r][$c]===0) $queue[]=[$r,$c];
$dirs=[[1,0],[-1,0],[0,1],[0,-1]];
while(!empty($queue)){
[$r,$c]=array_shift($queue);
foreach($dirs as[$dr,$dc]){
$nr=$r+$dr;$nc=$c+$dc;
if($nr>=0&&$nr<$R&&$nc>=0&&$nc<$C&&$rooms[$nr][$nc]===$INF){
$rooms[$nr][$nc]=$rooms[$r][$c]+1; $queue[]=[$nr,$nc];
}
}
}
}
Time: O(R Γ C) | Space: O(R Γ C)
Q9 β Redundant Connection
π§ How to Approach This
Step 1 β Recognize the pattern: Find the edge that creates a cycle in an undirected graph β Union-Find.
Step 2 β Brute force (what NOT to do): Remove each edge and check connectivity β O(EΒ·(V+E)).
Step 3 β Optimal insight: Union-Find: when union returns false (same component), that edge is redundant.
Step 4 β Algorithm:
1. Process edges in order
2. union(u, v): if already connected β return [u,v]
Step 5 β Edge cases: Multiple redundant edges β return the last one.
function findRedundantConnection(array $edges): array {
$n = count($edges);
$uf = new UnionFind($n + 1);
foreach ($edges as [$u, $v]) {
if (!$uf->union($u, $v)) return [$u, $v];
}
return [];
}
Time: O(n Β· Ξ±(n)) β O(n) | Space: O(n)
Q10 β Number of Connected Components
π§ How to Approach This
Step 1 β Recognize the pattern: Union-Find or DFS count of components.
Step 2 β Brute force (what NOT to do): Adjacency matrix DFS β O(VΒ²).
Step 3 β Optimal insight: Union-Find: start with n components; each successful union reduces by 1.
Step 4 β Algorithm:
1. Init count = n
2. For each edge: if union succeeds β count--
3. Return count
Step 5 β Edge cases: No edges β n components; all connected β 1.
function countComponents(int $n, array $edges): int {
$uf = new UnionFind($n);
$count = $n;
foreach ($edges as [$u, $v]) {
if ($uf->union($u, $v)) $count--;
}
return $count;
}
Time: O(E Β· Ξ±(n)) | Space: O(n)
Q11 β Graph Valid Tree
π§ How to Approach This
Step 1 β Recognize the pattern: Valid tree = n-1 edges + no cycle + connected.
Step 2 β Brute force (what NOT to do): Check every pair for connectivity β O(VΒ²).
Step 3 β Optimal insight: Union-Find: n-1 edges exactly + all unions succeed (no cycle).
Step 4 β Algorithm:
1. If edges != n-1 β false
2. Union all edges; if any union returns false β false (cycle)
3. Return true
Step 5 β Edge cases: n=1, edges=[] β true.
function validTree(int $n, array $edges): bool {
if (count($edges) !== $n - 1) return false;
$uf = new UnionFind($n);
foreach ($edges as [$u, $v]) {
if (!$uf->union($u, $v)) return false;
}
return true;
}
Time: O(E Β· Ξ±(n)) | Space: O(n)
Q12 β Word Ladder
π§ How to Approach This
Step 1 β Recognize the pattern: BFS on implicit graph (words differ by 1 char = edge); find shortest path.
Step 2 β Brute force (what NOT to do): Try all combinations at each step β exponential.
Step 3 β Optimal insight: BFS level by level; at each word try replacing each char with a-z.
Step 4 β Algorithm:
1. BFS queue with [word, length]
2. For each word: try all char replacements; if in wordList and unvisited β enqueue
3. Return length when endWord reached
Step 5 β Edge cases: endWord not in wordList; no path β 0.
function ladderLength(string $begin, string $end, array $wordList): int {
$set = array_flip($wordList);
if (!isset($set[$end])) return 0;
$queue = [[$begin, 1]];
while (!empty($queue)) {
[$word, $len] = array_shift($queue);
for ($i = 0; $i < strlen($word); $i++) {
for ($c = ord('a'); $c <= ord('z'); $c++) {
$next = substr($word, 0, $i) . chr($c) . substr($word, $i+1);
if ($next === $end) return $len + 1;
if (isset($set[$next])) {
unset($set[$next]);
$queue[] = [$next, $len + 1];
}
}
}
}
return 0;
}
Time: O(MΒ² Γ N) where M=word length, N=wordList size | Space: O(M Γ N)
Q13 β Shortest Path in Binary Matrix
π§ How to Approach This
Step 1 β Recognize the pattern: BFS in grid from (0,0) to (n-1,n-1) with 8-directional moves.
Step 2 β Brute force (what NOT to do): DFS β doesn't guarantee shortest path.
Step 3 β Optimal insight: BFS guarantees shortest path; level count = path length.
Step 4 β Algorithm:
1. If start or end is 1 β return -1
2. BFS with [r, c, dist]; explore 8 directions
Step 5 β Edge cases: 1Γ1 grid with 0 β 1.
function shortestPathBinaryMatrix(array $grid): int {
$n = count($grid);
if ($grid[0][0] === 1 || $grid[$n-1][$n-1] === 1) return -1;
$queue = [[0, 0, 1]]; $grid[0][0] = 1;
$dirs = [[-1,-1],[-1,0],[-1,1],[0,-1],[0,1],[1,-1],[1,0],[1,1]];
while (!empty($queue)) {
[$r,$c,$d] = array_shift($queue);
if ($r===$n-1 && $c===$n-1) return $d;
foreach ($dirs as [$dr,$dc]) {
$nr=$r+$dr; $nc=$c+$dc;
if ($nr>=0&&$nr<$n&&$nc>=0&&$nc<$n&&$grid[$nr][$nc]===0) {
$grid[$nr][$nc]=1; $queue[]=[$nr,$nc,$d+1];
}
}
}
return -1;
}
Time: O(nΒ²) | Space: O(nΒ²)
Q14 β Flood Fill
π§ How to Approach This
Step 1 β Recognize the pattern: DFS/BFS from source pixel; change all connected same-colored pixels.
Step 2 β Brute force (what NOT to do): Scan entire image each time β inefficient.
Step 3 β Optimal insight: DFS recursively recolors; early exit if new color equals old color.
Step 4 β Algorithm:
1. If color already equals new color β return image unchanged
2. DFS from (sr, sc): change color; recurse 4 directions for same old color
Step 5 β Edge cases: Source already has new color.
function floodFill(array $image, int $sr, int $sc, int $color): array {
$old = $image[$sr][$sc];
if ($old === $color) return $image;
$dfs = function(int $r, int $c) use (&$dfs, &$image, $old, $color): void {
if ($r<0||$r>=count($image)||$c<0||$c>=count($image[0])||$image[$r][$c]!==$old) return;
$image[$r][$c]=$color;
$dfs($r+1,$c);$dfs($r-1,$c);$dfs($r,$c+1);$dfs($r,$c-1);
};
$dfs($sr, $sc);
return $image;
}
Time: O(R Γ C) | Space: O(R Γ C)
Q15 β 01 Matrix (Distance to Nearest 0)
π§ How to Approach This
Step 1 β Recognize the pattern: Multi-source BFS from all 0s outward.
Step 2 β Brute force (what NOT to do): BFS from each 1 separately β O(RΒ²CΒ²).
Step 3 β Optimal insight: Enqueue all 0s at distance 0; BFS expands and sets distances for 1s.
Step 4 β Algorithm:
1. Init dist matrix: 0 for zeros, INF for ones
2. Enqueue all 0s; BFS β dist[nr][nc] = dist[r][c] + 1 if smaller
Step 5 β Edge cases: Entire matrix is 0s.
function updateMatrix(array $mat): array {
$R=count($mat);$C=count($mat[0]);$INF=PHP_INT_MAX;
$dist=array_fill(0,$R,array_fill(0,$C,$INF)); $queue=[];
for($r=0;$r<$R;$r++) for($c=0;$c<$C;$c++)
if($mat[$r][$c]===0){ $dist[$r][$c]=0; $queue[]=[$r,$c]; }
$dirs=[[1,0],[-1,0],[0,1],[0,-1]];
while(!empty($queue)){
[$r,$c]=array_shift($queue);
foreach($dirs as[$dr,$dc]){
$nr=$r+$dr;$nc=$c+$dc;
if($nr>=0&&$nr<$R&&$nc>=0&&$nc<$C&&$dist[$nr][$nc]>$dist[$r][$c]+1){
$dist[$nr][$nc]=$dist[$r][$c]+1; $queue[]=[$nr,$nc];
}
}
}
return $dist;
}
Time: O(R Γ C) | Space: O(R Γ C)
Q16 β Network Delay Time
π§ How to Approach This
Step 1 β Recognize the pattern: Shortest path from source to all nodes β Dijkstra's algorithm.
Step 2 β Brute force (what NOT to do): Bellman-Ford β O(VE), slower than Dijkstra.
Step 3 β Optimal insight: Dijkstra with min-heap; return max distance of all reachable nodes.
Step 4 β Algorithm:
1. Build adjacency list with weights
2. Dijkstra from k; track dist[] array
3. Return max(dist) if all reachable, else -1
Step 5 β Edge cases: Single node; disconnected graph.
function networkDelayTime(array $times, int $n, int $k): int {
$graph = array_fill(1, $n, []);
foreach ($times as [$u,$v,$w]) $graph[$u][] = [$v,$w];
$dist = array_fill(1, $n, PHP_INT_MAX); $dist[$k]=0;
$pq = [[0,$k]]; // [dist, node]
while (!empty($pq)) {
usort($pq, fn($a,$b)=>$a[0]-$b[0]);
[$d,$u] = array_shift($pq);
if ($d > $dist[$u]) continue;
foreach ($graph[$u] as [$v,$w]) {
if ($dist[$u]+$w < $dist[$v]) { $dist[$v]=$dist[$u]+$w; $pq[]=[$dist[$v],$v]; }
}
}
$max = max($dist);
return $max === PHP_INT_MAX ? -1 : $max;
}
Time: O((V + E) log V) | Space: O(V + E)
Q17 β Swim in Rising Water
π§ How to Approach This
Step 1 β Recognize the pattern: Find path from (0,0) to (n-1,n-1) minimizing the maximum elevation β Dijkstra/binary search.
Step 2 β Brute force (what NOT to do): BFS for each possible time t β O(nβ΄).
Step 3 β Optimal insight: Dijkstra treating cell value as cost; always expand min-cost cell.
Step 4 β Algorithm:
1. Priority queue of [max_so_far, r, c]
2. Pop min; if destination β return max_so_far
3. Push neighbors with max(current_max, neighbor_val)
Step 5 β Edge cases: 1Γ1 grid β grid[0][0].
function swimInWater(array $grid): int {
$n=count($grid); $visited=[];
$pq=[[($grid[0][0]),0,0]]; $dirs=[[1,0],[-1,0],[0,1],[0,-1]];
while(!empty($pq)){
usort($pq,fn($a,$b)=>$a[0]-$b[0]);
[$t,$r,$c]=array_shift($pq);
if($r===$n-1&&$c===$n-1) return $t;
if(isset($visited["$r,$c"])) continue;
$visited["$r,$c"]=true;
foreach($dirs as[$dr,$dc]){
$nr=$r+$dr;$nc=$c+$dc;
if($nr>=0&&$nr<$n&&$nc>=0&&$nc<$n&&!isset($visited["$nr,$nc"]))
$pq[]=[max($t,$grid[$nr][$nc]),$nr,$nc];
}
}
return -1;
}
Time: O(nΒ² log n) | Space: O(nΒ²)
Q18 β Alien Dictionary
π§ How to Approach This
Step 1 β Recognize the pattern: Build ordering constraints from adjacent words; topological sort.
Step 2 β Brute force (what NOT to do): Compare all pairs of words β O(NΒ²L).
Step 3 β Optimal insight: Adjacent words differ at first mismatch β that gives a directed edge; topological sort.
Step 4 β Algorithm:
1. Compare adjacent words; first mismatch gives edge (c1 β c2)
2. Topological sort on character graph
3. Invalid if prefix word comes after longer word
Step 5 β Edge cases: Single character; cycle in constraints.
function alienOrder(array $words): string {
$graph=[]; $indeg=[];
foreach($words as $w) foreach(str_split($w) as $c){ if(!isset($graph[$c])){ $graph[$c]=[]; $indeg[$c]=0; } }
for($i=0;$i<count($words)-1;$i++){
$w1=$words[$i]; $w2=$words[$i+1]; $len=min(strlen($w1),strlen($w2));
if(strlen($w1)>strlen($w2)&&str_starts_with($w1,$w2)) return '';
for($j=0;$j<$len;$j++){
if($w1[$j]!==$w2[$j]){ $graph[$w1[$j]][]=$w2[$j]; $indeg[$w2[$j]]++; break; }
}
}
$queue=array_keys(array_filter($indeg,fn($d)=>$d===0)); $order='';
while(!empty($queue)){
$c=array_shift($queue); $order.=$c;
foreach($graph[$c] as $nb){ if(--$indeg[$nb]===0) $queue[]=$nb; }
}
return strlen($order)===count($graph) ? $order : '';
}
Time: O(C) where C = total characters | Space: O(1) since β€26 chars
Q19 β Minimum Height Trees
π§ How to Approach This
Step 1 β Recognize the pattern: Root that minimizes height = centroid(s) of tree; prune leaves iteratively.
Step 2 β Brute force (what NOT to do): BFS from every node as root β O(nΒ²).
Step 3 β Optimal insight: Iteratively remove leaf nodes (degree=1); last 1-2 nodes are centroids.
Step 4 β Algorithm:
1. Build adjacency list + degree array
2. Enqueue all leaves (degree=1)
3. Remove leaves; enqueue new leaves; repeat until β€ 2 nodes
Step 5 β Edge cases: n=1 β [0]; n=2 β [0,1].
function findMinHeightTrees(int $n, array $edges): array {
if($n===1) return [0];
$graph=array_fill(0,$n,[]); $degree=array_fill(0,$n,0);
foreach($edges as[$u,$v]){ $graph[$u][]=$v; $graph[$v][]=$u; $degree[$u]++; $degree[$v]++; }
$leaves=array_keys(array_filter($degree,fn($d)=>$d===1));
$rem=$n;
while($rem>2){
$rem-=count($leaves); $next=[];
foreach($leaves as $l) foreach($graph[$l] as $nb){ if(--$degree[$nb]===1) $next[]=$nb; }
$leaves=$next;
}
return $leaves;
}
Time: O(n) | Space: O(n)
Q20 β Is Graph Bipartite?
π§ How to Approach This
Step 1 β Recognize the pattern: 2-coloring β alternate colors on BFS/DFS; conflict = not bipartite.
Step 2 β Brute force (what NOT to do): Try all 2^n colorings β exponential.
Step 3 β Optimal insight: BFS 2-coloring; if neighbor has same color β not bipartite.
Step 4 β Algorithm:
1. Color array init -1
2. BFS: color node 0; neighbors get color 1-current
3. Conflict β return false
Step 5 β Edge cases: Disconnected graph β must check all components.
function isBipartite(array $graph): bool {
$n=count($graph); $color=array_fill(0,$n,-1);
for($i=0;$i<$n;$i++){
if($color[$i]!==-1) continue;
$queue=[$i]; $color[$i]=0;
while(!empty($queue)){
$u=array_shift($queue);
foreach($graph[$u] as $v){
if($color[$v]===-1){ $color[$v]=1-$color[$u]; $queue[]=$v; }
elseif($color[$v]===$color[$u]) return false;
}
}
}
return true;
}
Time: O(V + E) | Space: O(V)
Q21 β Find Eventual Safe States
π§ How to Approach This
Step 1 β Recognize the pattern: Safe node = node that does NOT lead to a cycle; reverse graph topological sort.
Step 2 β Brute force (what NOT to do): DFS from every node checking for cycles β O(VΒ²).
Step 3 β Optimal insight: Reverse graph + Kahn's; nodes with 0 outdegree in reverse = safe.
Step 4 β Algorithm:
1. Build reverse graph; compute outdegree of original
2. Kahn's on reverse graph starting from terminal nodes (outdegree=0 in original)
Step 5 β Edge cases: No cycles β all nodes safe.
function eventualSafeNodes(array $graph): array {
$n=count($graph); $rev=array_fill(0,$n,[]); $outdeg=array_fill(0,$n,0);
foreach($graph as $u=>$neighbors) foreach($neighbors as $v){ $rev[$v][]=$u; $outdeg[$u]++; }
$queue=array_keys(array_filter($outdeg,fn($d)=>$d===0)); $safe=[];
while(!empty($queue)){
$u=array_shift($queue); $safe[]=$u;
foreach($rev[$u] as $v){ if(--$outdeg[$v]===0) $queue[]=$v; }
}
sort($safe);
return $safe;
}
Time: O(V + E) | Space: O(V + E)
Q22 β Jump Game III
π§ How to Approach This
Step 1 β Recognize the pattern: BFS/DFS reachability from start; can we reach any index with value 0?
Step 2 β Brute force (what NOT to do): Recursion without memoization β exponential revisits.
Step 3 β Optimal insight: BFS from start index; at each index jump Β±arr[i]; track visited.
Step 4 β Algorithm:
1. BFS queue starting at start
2. From index i: check i+arr[i] and i-arr[i]
3. If arr[index] === 0 β return true
Step 5 β Edge cases: arr[start] === 0 immediately true.
function canReach(array $arr, int $start): bool {
$n=count($arr); $visited=[]; $queue=[$start];
while(!empty($queue)){
$i=array_shift($queue);
if($arr[$i]===0) return true;
if(isset($visited[$i])) continue;
$visited[$i]=true;
foreach([$i+$arr[$i], $i-$arr[$i]] as $next)
if($next>=0&&$next<$n&&!isset($visited[$next])) $queue[]=$next;
}
return false;
}
Time: O(n) | Space: O(n)
Q23 β Evaluate Division
π§ How to Approach This
Step 1 β Recognize the pattern: Build weighted directed graph; BFS to find path product between nodes.
Step 2 β Brute force (what NOT to do): Precompute all pairs β O(VΒ³) with Floyd-Warshall.
Step 3 β Optimal insight: Build graph with a/b = value and b/a = 1/value; BFS for each query multiplying edge weights.
Step 4 β Algorithm:
1. Build weighted adjacency list from equations
2. For each query: BFS from numerator to denominator; multiply weights along path
Step 5 β Edge cases: Unknown variables β -1; same variable a/a β 1.
function calcEquation(array $equations, array $values, array $queries): array {
$graph=[];
foreach($equations as $i=>[$a,$b]){ $graph[$a][$b]=$values[$i]; $graph[$b][$a]=1/$values[$i]; }
$bfs=function(string $src, string $dst) use(&$graph): float {
if(!isset($graph[$src])||!isset($graph[$dst])) return -1.0;
if($src===$dst) return 1.0;
$visited=[$src=>true]; $queue=[[$src,1.0]];
while(!empty($queue)){
[$node,$prod]=array_shift($queue);
foreach(($graph[$node]??[]) as $nb=>$w){
if($nb===$dst) return $prod*$w;
if(!isset($visited[$nb])){ $visited[$nb]=true; $queue[]=[$nb,$prod*$w]; }
}
}
return -1.0;
};
return array_map(fn($q)=>$bfs($q[0],$q[1]), $queries);
}
Time: O(Q Β· (V + E)) | Space: O(V + E)
Q24 β Sequence Reconstruction
π§ How to Approach This
Step 1 β Recognize the pattern: Topological sort; check if only ONE unique ordering exists.
Step 2 β Brute force (what NOT to do): Generate all topological orderings β exponential.
Step 3 β Optimal insight: Kahn's; at each step the queue must have exactly 1 node (unique ordering).
Step 4 β Algorithm:
1. Build graph from sequences; track indegree
2. Kahn's: if queue size > 1 at any point β false
3. If resulting order matches org β true
Step 5 β Edge cases: org has elements not in seqs β false.
function sequenceReconstruction(array $org, array $seqs): bool {
$graph=[]; $indeg=[];
foreach($seqs as $seq) foreach($seq as $v){ if(!isset($graph[$v])){ $graph[$v]=[]; $indeg[$v]=0; } }
foreach($seqs as $seq) for($i=0;$i<count($seq)-1;$i++){
[$u,$v]=[$seq[$i],$seq[$i+1]];
if(!in_array($v,$graph[$u])){ $graph[$u][]=$v; $indeg[$v]++; }
}
$queue=array_keys(array_filter($indeg,fn($d)=>$d===0)); $idx=0;
while(count($queue)===1){
$u=array_shift($queue);
if($org[$idx++]!==$u) return false;
foreach($graph[$u] as $v){ if(--$indeg[$v]===0) $queue[]=$v; }
}
return $idx===count($org)&&empty($queue);
}
Time: O(V + E) | Space: O(V + E)
Q25 β Minimum Cost to Connect All Points
π§ How to Approach This
Step 1 β Recognize the pattern: Minimum Spanning Tree (MST) on complete graph with Manhattan distance edges.
Step 2 β Brute force (what NOT to do): Try all spanning trees β exponential.
Step 3 β Optimal insight: Prim's MST: greedily add nearest unvisited point.
Step 4 β Algorithm:
1. Init dist[] = INF; dist[0] = 0
2. Greedily pick min-dist unvisited node; mark visited
3. Update dist[] of neighbors with manhattan distance
Step 5 β Edge cases: Single point β 0.
function minCostConnectPoints(array $points): int {
$n=count($points); $dist=array_fill(0,$n,PHP_INT_MAX); $dist[0]=0;
$visited=array_fill(0,$n,false); $total=0;
for($i=0;$i<$n;$i++){
$u=-1;
for($j=0;$j<$n;$j++) if(!$visited[$j]&&($u===-1||$dist[$j]<$dist[$u])) $u=$j;
$visited[$u]=true; $total+=$dist[$u];
for($v=0;$v<$n;$v++){
if(!$visited[$v]){
$d=abs($points[$u][0]-$points[$v][0])+abs($points[$u][1]-$points[$v][1]);
if($d<$dist[$v]) $dist[$v]=$d;
}
}
}
return $total;
}
Time: O(nΒ²) | Space: O(n)