import type { AlgorithmDefinition, GridCellState } from '@/lib/visualizations/types'; /** * Number of Islands (LeetCode 200) * * Grid: * ["1","1","0","0","0"] * ["1","1","0","0","0"] * ["0","0","1","0","0"] * ["0","0","0","1","1"] * * Output: 3 islands * Island 1: (0,0), (0,1), (1,0), (1,1) * Island 2: (2,2) * Island 3: (3,3), (3,4) */ // Grid data (4 rows x 5 cols) const GRID = [ ['1', '1', '0', '0', '0'], ['1', '1', '0', '0', '0'], ['0', '0', '1', '0', '0'], ['0', '0', '0', '1', '1'], ]; // Helper to create grid state with cell highlighting function createIslandGrid( visitedCells: Set, currentCell?: string, exploringCells?: Set, label = 'Island Grid' ) { const cells: GridCellState[][] = GRID.map((row, r) => row.map((val, c) => { const key = `${r}-${c}`; let state: GridCellState['state'] = val === '0' ? 'dimmed' : 'normal'; if (visitedCells.has(key)) state = 'success'; if (exploringCells?.has(key)) state = 'computing'; if (key === currentCell) state = 'highlighted'; return { id: `cell-${r}-${c}`, value: val, row: r, col: c, state }; }) ); return { id: 'island-grid', cells, rowLabels: [0, 1, 2, 3], colLabels: [0, 1, 2, 3, 4], label, }; } // Helper to create initial grid showing all land/water function createInitialGrid() { const cells: GridCellState[][] = GRID.map((row, r) => row.map((val, c) => ({ id: `cell-${r}-${c}`, value: val, row: r, col: c, state: val === '0' ? 'dimmed' : 'normal', })) ); return { id: 'island-grid', cells, rowLabels: [0, 1, 2, 3], colLabels: [0, 1, 2, 3, 4], label: 'Island Grid', }; } export const numberOfIslandsAlgorithm: AlgorithmDefinition = { id: 'number-of-islands', title: 'Number of Islands - Matrix Traversal', slug: 'number-of-islands', pattern: { name: 'Matrix Traversal', description: 'Explore a 2D grid using DFS or BFS to find connected components, paths, or regions.', }, problemStatement: 'Given a 2D grid of \'1\'s (land) and \'0\'s (water), count the number of islands. An island is surrounded by water and formed by connecting adjacent lands horizontally or vertically.', intuition: 'Think of the grid as a map. Each unvisited land cell could be the start of a new island. Use DFS to explore all connected land cells from a starting point, marking them visited. Each time we start a new DFS from an unvisited land cell, we\'ve found a new island.', code: { language: 'python', code: `def numIslands(grid: list[list[str]]) -> int: if not grid: return 0 rows, cols = len(grid), len(grid[0]) count = 0 def dfs(r: int, c: int): if r < 0 or r >= rows or c < 0 or c >= cols: return if grid[r][c] == '0': return grid[r][c] = '0' # Mark visited dfs(r + 1, c) # Down dfs(r - 1, c) # Up dfs(r, c + 1) # Right dfs(r, c - 1) # Left for r in range(rows): for c in range(cols): if grid[r][c] == '1': count += 1 dfs(r, c) return count`, }, initialExample: { input: { grid: [ ['1', '1', '0', '0', '0'], ['1', '1', '0', '0', '0'], ['0', '0', '1', '0', '0'], ['0', '0', '0', '1', '1'], ], }, expected: 3, }, steps: [ // ========================================== // Phase 1: Problem (2 steps) // ========================================== { id: 'problem-1', phase: 'problem', explanation: 'We have a 4x5 grid where \'1\' represents land and \'0\' represents water. Our goal is to count distinct islands (connected land regions).', dataState: { arrays: [], pointers: [], variables: [], calculations: [], grids: [createInitialGrid()], }, }, { id: 'problem-2', phase: 'problem', explanation: 'Land cells are connected horizontally or vertically (not diagonally). Looking at this grid, we can visually spot 3 separate islands.', dataState: { arrays: [], pointers: [], variables: [ { id: 'islands', name: 'Expected', value: '3 islands' }, ], calculations: [], grids: [createInitialGrid()], }, }, // ========================================== // Phase 2: Intuition (3 steps) // ========================================== { id: 'intuition-1', phase: 'intuition', explanation: 'Strategy: Scan the grid cell by cell. When we find an unvisited land cell (\'1\'), we\'ve discovered a new island. Increment count and explore all connected land using DFS.', dataState: { arrays: [], pointers: [], variables: [ { id: 'count', name: 'islandCount', value: 0 }, ], calculations: [], grids: [createIslandGrid(new Set(), '0-0')], }, }, { id: 'intuition-2', phase: 'intuition', explanation: 'DFS explores in 4 directions (up, down, left, right). As we visit each land cell, we mark it as visited by changing \'1\' to \'0\'. This prevents counting the same island twice.', dataState: { arrays: [], pointers: [], variables: [ { id: 'count', name: 'islandCount', value: 1 }, ], calculations: [ { id: 'calc-1', expression: 'DFS: (0,0) → neighbors', result: '4 directions', position: 'above' }, ], grids: [createIslandGrid(new Set(), '0-0', new Set(['0-1', '1-0']))], }, }, { id: 'intuition-3', phase: 'intuition', explanation: 'Key insight: Each time we start DFS from a new unvisited \'1\', we\'ve found a complete new island. The DFS call explores the entire island before returning.', dataState: { arrays: [], pointers: [], variables: [ { id: 'count', name: 'islandCount', value: 1 }, ], calculations: [], grids: [createIslandGrid(new Set(['0-0', '0-1', '1-0', '1-1']))], }, }, // ========================================== // Phase 3: Pattern (2 steps) // ========================================== { id: 'pattern-1', phase: 'pattern', explanation: 'Matrix Traversal pattern: Treat the grid as an implicit graph where each cell is a node, and adjacent cells are edges. DFS/BFS finds connected components.', dataState: { arrays: [], pointers: [], variables: [ { id: 'pattern', name: 'Pattern', value: 'Connected Components' }, ], calculations: [], grids: [createInitialGrid()], }, }, { id: 'pattern-2', phase: 'pattern', explanation: 'Time: O(rows × cols) - we visit each cell at most once. Space: O(rows × cols) in worst case for DFS recursion stack (all land).', dataState: { arrays: [], pointers: [], variables: [ { id: 'time', name: 'Time', value: 'O(m × n)' }, { id: 'space', name: 'Space', value: 'O(m × n)' }, ], calculations: [], grids: [createInitialGrid()], }, }, // ========================================== // Phase 4: Code (4 steps) // ========================================== { id: 'code-1', phase: 'code', explanation: 'Initialize: Get grid dimensions, set island count to 0. We\'ll modify the grid in-place to track visited cells.', codeLine: 5, codeHighlightLines: [2, 3, 4, 5, 6], dataState: { arrays: [], pointers: [], variables: [ { id: 'rows', name: 'rows', value: 4 }, { id: 'cols', name: 'cols', value: 5 }, { id: 'count', name: 'count', value: 0 }, ], calculations: [], grids: [createInitialGrid()], }, }, { id: 'code-2', phase: 'code', explanation: 'DFS function: Base cases return early if out of bounds or cell is water. Otherwise, mark cell visited and recurse in all 4 directions.', codeLine: 12, codeHighlightLines: [8, 9, 10, 11, 12, 13, 14, 15, 16, 17], dataState: { arrays: [], pointers: [], variables: [], calculations: [ { id: 'calc-1', expression: 'dfs(r, c)', result: 'explore & mark', position: 'above' }, ], grids: [createInitialGrid()], }, }, { id: 'code-3', phase: 'code', explanation: 'Main loop: Scan every cell. When we find \'1\' (unvisited land), increment count and run DFS to mark entire island as visited.', codeLine: 22, codeHighlightLines: [19, 20, 21, 22, 23], dataState: { arrays: [], pointers: [], variables: [], calculations: [], grids: [createIslandGrid(new Set(), '0-0')], }, }, { id: 'code-4', phase: 'code', explanation: 'Return the final count. Each DFS call discovers one complete island, so count equals the number of islands.', codeLine: 25, dataState: { arrays: [], pointers: [], variables: [ { id: 'count', name: 'count', value: '?' }, ], calculations: [], grids: [createInitialGrid()], }, }, // ========================================== // Phase 5: Execution (~12 steps) // ========================================== { id: 'exec-1', phase: 'execution', explanation: 'Start scanning at (0,0). Found \'1\' - this is unvisited land! Increment count to 1 and start DFS to explore this island.', codeLine: 21, decision: { question: 'Is cell (0,0) land?', answer: 'Yes, grid[0][0] = \'1\'', action: 'New island found! count++, start DFS', }, dataState: { arrays: [], pointers: [], variables: [ { id: 'r', name: 'r', value: 0 }, { id: 'c', name: 'c', value: 0 }, { id: 'count', name: 'count', value: 1 }, ], calculations: [], grids: [createIslandGrid(new Set(), '0-0')], gridPointers: [ { id: 'ptr-curr', name: 'current', row: 0, col: 0, color: 'current' }, ], }, }, { id: 'exec-2', phase: 'execution', explanation: 'DFS from (0,0): Mark as visited, explore neighbors. Cell (0,1) is land - recurse there. Cell (1,0) is also land.', codeLine: 13, dataState: { arrays: [], pointers: [], variables: [ { id: 'count', name: 'count', value: 1 }, ], calculations: [ { id: 'calc-1', expression: 'dfs(0,0) → dfs(0,1), dfs(1,0)', result: 'exploring', position: 'above' }, ], grids: [createIslandGrid(new Set(['0-0']), undefined, new Set(['0-1', '1-0']))], gridPointers: [ { id: 'ptr-curr', name: 'visited', row: 0, col: 0, color: 'result' }, ], }, }, { id: 'exec-3', phase: 'execution', explanation: 'DFS explores (0,1), then (1,0), then (1,1). All 4 cells of island 1 are now marked as visited (green).', codeLine: 13, dataState: { arrays: [], pointers: [], variables: [ { id: 'count', name: 'count', value: 1 }, ], calculations: [], grids: [createIslandGrid(new Set(['0-0', '0-1', '1-0', '1-1']))], }, }, { id: 'exec-4', phase: 'execution', explanation: 'DFS complete for island 1. Continue scanning: (0,2), (0,3), (0,4) are all water (\'0\'). Skip them.', codeLine: 20, dataState: { arrays: [], pointers: [], variables: [ { id: 'r', name: 'r', value: 0 }, { id: 'c', name: 'c', value: 2 }, { id: 'count', name: 'count', value: 1 }, ], calculations: [], grids: [createIslandGrid(new Set(['0-0', '0-1', '1-0', '1-1']), '0-2')], gridPointers: [ { id: 'ptr-scan', name: 'scan', row: 0, col: 2, color: 'current' }, ], }, }, { id: 'exec-5', phase: 'execution', explanation: 'Scanning row 1: (1,0) and (1,1) already visited. (1,2), (1,3), (1,4) are water. Continue to row 2.', codeLine: 20, dataState: { arrays: [], pointers: [], variables: [ { id: 'r', name: 'r', value: 1 }, { id: 'count', name: 'count', value: 1 }, ], calculations: [], grids: [createIslandGrid(new Set(['0-0', '0-1', '1-0', '1-1']))], }, }, { id: 'exec-6', phase: 'execution', explanation: 'Scan (2,0), (2,1) - water. At (2,2): Found \'1\'! This is a new unvisited land cell. Increment count to 2.', codeLine: 21, decision: { question: 'Is cell (2,2) land?', answer: 'Yes, grid[2][2] = \'1\'', action: 'New island found! count++', }, dataState: { arrays: [], pointers: [], variables: [ { id: 'r', name: 'r', value: 2 }, { id: 'c', name: 'c', value: 2 }, { id: 'count', name: 'count', value: 2 }, ], calculations: [], grids: [createIslandGrid(new Set(['0-0', '0-1', '1-0', '1-1']), '2-2')], gridPointers: [ { id: 'ptr-curr', name: 'current', row: 2, col: 2, color: 'current' }, ], }, }, { id: 'exec-7', phase: 'execution', explanation: 'DFS from (2,2): Check all 4 neighbors - all are water or out of bounds. This is a single-cell island. Mark visited.', codeLine: 13, dataState: { arrays: [], pointers: [], variables: [ { id: 'count', name: 'count', value: 2 }, ], calculations: [ { id: 'calc-1', expression: 'dfs(2,2) neighbors', result: 'all water', position: 'above' }, ], grids: [createIslandGrid(new Set(['0-0', '0-1', '1-0', '1-1', '2-2']))], }, }, { id: 'exec-8', phase: 'execution', explanation: 'Continue scanning row 2 and row 3: (2,3), (2,4), (3,0), (3,1), (3,2) are all water. Skip them.', codeLine: 20, dataState: { arrays: [], pointers: [], variables: [ { id: 'r', name: 'r', value: 3 }, { id: 'c', name: 'c', value: 2 }, { id: 'count', name: 'count', value: 2 }, ], calculations: [], grids: [createIslandGrid(new Set(['0-0', '0-1', '1-0', '1-1', '2-2']), '3-2')], gridPointers: [ { id: 'ptr-scan', name: 'scan', row: 3, col: 2, color: 'current' }, ], }, }, { id: 'exec-9', phase: 'execution', explanation: 'At (3,3): Found \'1\'! This is another new island. Increment count to 3 and start DFS.', codeLine: 21, decision: { question: 'Is cell (3,3) land?', answer: 'Yes, grid[3][3] = \'1\'', action: 'New island found! count++', }, dataState: { arrays: [], pointers: [], variables: [ { id: 'r', name: 'r', value: 3 }, { id: 'c', name: 'c', value: 3 }, { id: 'count', name: 'count', value: 3 }, ], calculations: [], grids: [createIslandGrid(new Set(['0-0', '0-1', '1-0', '1-1', '2-2']), '3-3')], gridPointers: [ { id: 'ptr-curr', name: 'current', row: 3, col: 3, color: 'current' }, ], }, }, { id: 'exec-10', phase: 'execution', explanation: 'DFS from (3,3): Mark visited, explore neighbors. Found (3,4) is also land! Recurse to explore it.', codeLine: 13, dataState: { arrays: [], pointers: [], variables: [ { id: 'count', name: 'count', value: 3 }, ], calculations: [ { id: 'calc-1', expression: 'dfs(3,3) → dfs(3,4)', result: 'found neighbor', position: 'above' }, ], grids: [createIslandGrid(new Set(['0-0', '0-1', '1-0', '1-1', '2-2', '3-3']), undefined, new Set(['3-4']))], gridPointers: [ { id: 'ptr-curr', name: 'exploring', row: 3, col: 4, color: 'current' }, ], }, }, { id: 'exec-11', phase: 'execution', explanation: 'DFS visits (3,4): Mark visited. No more land neighbors. Island 3 complete with cells (3,3) and (3,4).', codeLine: 13, dataState: { arrays: [], pointers: [], variables: [ { id: 'count', name: 'count', value: 3 }, ], calculations: [], grids: [createIslandGrid(new Set(['0-0', '0-1', '1-0', '1-1', '2-2', '3-3', '3-4']))], }, }, { id: 'exec-12', phase: 'execution', explanation: 'Scan complete! All cells visited. We found 3 islands: (1) 4 cells top-left, (2) 1 cell middle, (3) 2 cells bottom-right.', codeLine: 25, dataState: { arrays: [], pointers: [], variables: [ { id: 'count', name: 'count', value: 3, derivation: '3 connected components' }, ], calculations: [ { id: 'calc-1', expression: 'return count', result: '3', position: 'above' }, ], grids: [createIslandGrid(new Set(['0-0', '0-1', '1-0', '1-1', '2-2', '3-3', '3-4']), undefined, undefined, 'Result: 3 Islands')], }, }, ], };