429 lines
15 KiB
TypeScript
429 lines
15 KiB
TypeScript
import type { AlgorithmDefinition } from '@/lib/visualizations/types';
|
|
|
|
/**
|
|
* Tree structure (level order: [4, 2, 6, 1, 3, 5, 7]):
|
|
*
|
|
* 4 (root)
|
|
* / \
|
|
* 2 6
|
|
* / \ / \
|
|
* 1 3 5 7
|
|
*
|
|
* DFS (Preorder) output: [4, 2, 1, 3, 6, 5, 7]
|
|
*/
|
|
|
|
// Node IDs for our tree
|
|
const NODE_4 = 'n4';
|
|
const NODE_2 = 'n2';
|
|
const NODE_6 = 'n6';
|
|
const NODE_1 = 'n1';
|
|
const NODE_3 = 'n3';
|
|
const NODE_5 = 'n5';
|
|
const NODE_7 = 'n7';
|
|
|
|
// Helper to create tree state with specific node states
|
|
function createTreeState(nodeStates: Record<string, 'normal' | 'current' | 'visiting' | 'visited' | 'highlighted'>) {
|
|
return {
|
|
id: 'tree',
|
|
label: 'Binary Search Tree',
|
|
rootId: NODE_4,
|
|
nodes: [
|
|
{ id: NODE_4, value: 4, state: nodeStates[NODE_4] ?? 'normal', left: NODE_2, right: NODE_6 },
|
|
{ id: NODE_2, value: 2, state: nodeStates[NODE_2] ?? 'normal', left: NODE_1, right: NODE_3 },
|
|
{ id: NODE_6, value: 6, state: nodeStates[NODE_6] ?? 'normal', left: NODE_5, right: NODE_7 },
|
|
{ id: NODE_1, value: 1, state: nodeStates[NODE_1] ?? 'normal', left: null, right: null },
|
|
{ id: NODE_3, value: 3, state: nodeStates[NODE_3] ?? 'normal', left: null, right: null },
|
|
{ id: NODE_5, value: 5, state: nodeStates[NODE_5] ?? 'normal', left: null, right: null },
|
|
{ id: NODE_7, value: 7, state: nodeStates[NODE_7] ?? 'normal', left: null, right: null },
|
|
],
|
|
};
|
|
}
|
|
|
|
// Helper to create stack state
|
|
function createStackState(values: number[], highlightTop = false) {
|
|
return {
|
|
id: 'dfs-stack',
|
|
label: 'Stack',
|
|
elements: values.map((v, i) => ({
|
|
id: `stack-${v}-${i}`,
|
|
value: v,
|
|
state: (highlightTop && i === values.length - 1 ? 'highlighted' : 'normal') as 'normal' | 'highlighted',
|
|
})),
|
|
};
|
|
}
|
|
|
|
// Helper to create output array state
|
|
function createOutputState(values: (number | null)[], highlightIndex?: number) {
|
|
const maxLen = 7;
|
|
const elements = [];
|
|
for (let i = 0; i < maxLen; i++) {
|
|
const val = values[i];
|
|
elements.push({
|
|
value: val ?? 0,
|
|
index: i,
|
|
state: (val === null ? 'dimmed' : i === highlightIndex ? 'highlighted' : 'success') as 'normal' | 'highlighted' | 'dimmed' | 'success' | 'comparing',
|
|
});
|
|
}
|
|
return {
|
|
id: 'output',
|
|
label: 'Output Array',
|
|
elements,
|
|
};
|
|
}
|
|
|
|
export const dfsAlgorithm: AlgorithmDefinition = {
|
|
id: 'dfs',
|
|
title: 'Depth-First Search (DFS)',
|
|
slug: 'dfs',
|
|
pattern: {
|
|
name: 'DFS',
|
|
description:
|
|
'Use a stack to explore as deep as possible along each branch before backtracking.',
|
|
},
|
|
problemStatement:
|
|
'Given a binary tree, traverse it depth-first (preorder: Root-Left-Right) and return the values in the order visited.',
|
|
intuition:
|
|
'DFS explores one branch completely before backtracking. Using a stack (LIFO) naturally achieves this: we pop a node, process it, then push its children. By pushing right before left, we ensure left children are processed first.',
|
|
code: {
|
|
language: 'python',
|
|
code: `def dfs(root):
|
|
if not root:
|
|
return []
|
|
|
|
result = []
|
|
stack = [root]
|
|
|
|
while stack:
|
|
# Pop top node
|
|
node = stack.pop()
|
|
result.append(node.val)
|
|
|
|
# Push right first, then left (so left is popped first)
|
|
if node.right:
|
|
stack.append(node.right)
|
|
if node.left:
|
|
stack.append(node.left)
|
|
|
|
return result`,
|
|
},
|
|
initialExample: {
|
|
input: { tree: [4, 2, 6, 1, 3, 5, 7] },
|
|
expected: [4, 2, 1, 3, 6, 5, 7],
|
|
},
|
|
steps: [
|
|
// ==========================================
|
|
// Phase 1: Problem (2 steps)
|
|
// ==========================================
|
|
{
|
|
id: 'problem-1',
|
|
phase: 'problem',
|
|
explanation:
|
|
'We have a binary tree. Our goal is to visit all nodes depth-first: go as deep as possible down each branch before backtracking.',
|
|
dataState: {
|
|
arrays: [],
|
|
pointers: [],
|
|
variables: [],
|
|
calculations: [],
|
|
trees: [createTreeState({})],
|
|
stacks: [createStackState([])],
|
|
},
|
|
},
|
|
{
|
|
id: 'problem-2',
|
|
phase: 'problem',
|
|
explanation:
|
|
'Preorder DFS visits Root, then Left subtree, then Right subtree. Expected output: [4, 2, 1, 3, 6, 5, 7].',
|
|
dataState: {
|
|
arrays: [createOutputState([null, null, null, null, null, null, null])],
|
|
pointers: [],
|
|
variables: [],
|
|
calculations: [],
|
|
trees: [createTreeState({ [NODE_4]: 'highlighted' })],
|
|
stacks: [createStackState([])],
|
|
},
|
|
},
|
|
|
|
// ==========================================
|
|
// Phase 2: Intuition (3 steps)
|
|
// ==========================================
|
|
{
|
|
id: 'intuition-1',
|
|
phase: 'intuition',
|
|
explanation:
|
|
'DFS uses a stack (LIFO). We start by pushing the root, then repeatedly: pop a node, process it, and push its children.',
|
|
dataState: {
|
|
arrays: [createOutputState([null, null, null, null, null, null, null])],
|
|
pointers: [],
|
|
variables: [],
|
|
calculations: [],
|
|
trees: [createTreeState({ [NODE_4]: 'current' })],
|
|
stacks: [createStackState([4], true)],
|
|
},
|
|
},
|
|
{
|
|
id: 'intuition-2',
|
|
phase: 'intuition',
|
|
explanation:
|
|
'Key trick: Push right child BEFORE left child. Since stack is LIFO, left child will be popped (and processed) first!',
|
|
dataState: {
|
|
arrays: [createOutputState([4, null, null, null, null, null, null], 0)],
|
|
pointers: [],
|
|
variables: [],
|
|
calculations: [],
|
|
trees: [createTreeState({ [NODE_4]: 'visited', [NODE_2]: 'visiting', [NODE_6]: 'visiting' })],
|
|
stacks: [createStackState([6, 2], true)],
|
|
},
|
|
},
|
|
{
|
|
id: 'intuition-3',
|
|
phase: 'intuition',
|
|
explanation:
|
|
'The stack remembers where to backtrack. When we finish the left subtree, the right subtree is waiting on the stack.',
|
|
dataState: {
|
|
arrays: [createOutputState([4, 2, null, null, null, null, null])],
|
|
pointers: [],
|
|
variables: [],
|
|
calculations: [],
|
|
trees: [createTreeState({ [NODE_4]: 'visited', [NODE_2]: 'visited', [NODE_1]: 'visiting', [NODE_3]: 'visiting', [NODE_6]: 'visiting' })],
|
|
stacks: [createStackState([6, 3, 1], true)],
|
|
},
|
|
},
|
|
|
|
// ==========================================
|
|
// Phase 3: Pattern (2 steps)
|
|
// ==========================================
|
|
{
|
|
id: 'pattern-1',
|
|
phase: 'pattern',
|
|
explanation:
|
|
'DFS pattern: (1) Initialize stack with root, (2) While stack not empty: pop, process, push children (right then left).',
|
|
dataState: {
|
|
arrays: [createOutputState([null, null, null, null, null, null, null])],
|
|
pointers: [],
|
|
variables: [],
|
|
calculations: [],
|
|
trees: [createTreeState({})],
|
|
stacks: [createStackState([])],
|
|
},
|
|
},
|
|
{
|
|
id: 'pattern-2',
|
|
phase: 'pattern',
|
|
explanation:
|
|
'The stack ensures depth-first order: we completely explore one subtree before moving to its sibling.',
|
|
dataState: {
|
|
arrays: [createOutputState([null, null, null, null, null, null, null])],
|
|
pointers: [],
|
|
variables: [
|
|
{ id: 'stack', name: 'stack', value: 'LIFO' },
|
|
{ id: 'result', name: 'result', value: '[]' },
|
|
],
|
|
calculations: [],
|
|
trees: [createTreeState({ [NODE_4]: 'current' })],
|
|
stacks: [createStackState([4], true)],
|
|
},
|
|
},
|
|
|
|
// ==========================================
|
|
// Phase 4: Code (3 steps)
|
|
// ==========================================
|
|
{
|
|
id: 'code-1',
|
|
phase: 'code',
|
|
explanation:
|
|
'Initialize: empty result list, stack containing just the root node.',
|
|
codeLine: 5,
|
|
codeHighlightLines: [5, 6, 7],
|
|
dataState: {
|
|
arrays: [createOutputState([null, null, null, null, null, null, null])],
|
|
pointers: [],
|
|
variables: [
|
|
{ id: 'result', name: 'result', value: '[]' },
|
|
],
|
|
calculations: [],
|
|
trees: [createTreeState({ [NODE_4]: 'current' })],
|
|
stacks: [createStackState([4], true)],
|
|
},
|
|
},
|
|
{
|
|
id: 'code-2',
|
|
phase: 'code',
|
|
explanation:
|
|
'Main loop: While stack is not empty, pop the top node and add its value to result.',
|
|
codeLine: 10,
|
|
codeHighlightLines: [9, 10, 11, 12],
|
|
dataState: {
|
|
arrays: [createOutputState([4, null, null, null, null, null, null], 0)],
|
|
pointers: [],
|
|
variables: [],
|
|
calculations: [],
|
|
trees: [createTreeState({ [NODE_4]: 'visited' })],
|
|
stacks: [createStackState([])],
|
|
},
|
|
},
|
|
{
|
|
id: 'code-3',
|
|
phase: 'code',
|
|
explanation:
|
|
'Push right child first, then left child. LIFO means left will be popped first, ensuring we go left before right.',
|
|
codeLine: 15,
|
|
codeHighlightLines: [14, 15, 16, 17, 18],
|
|
dataState: {
|
|
arrays: [createOutputState([4, null, null, null, null, null, null])],
|
|
pointers: [],
|
|
variables: [],
|
|
calculations: [],
|
|
trees: [createTreeState({ [NODE_4]: 'visited', [NODE_2]: 'visiting', [NODE_6]: 'visiting' })],
|
|
stacks: [createStackState([6, 2], true)],
|
|
},
|
|
},
|
|
|
|
// ==========================================
|
|
// Phase 5: Execution (10 steps)
|
|
// ==========================================
|
|
{
|
|
id: 'exec-1',
|
|
phase: 'execution',
|
|
explanation:
|
|
'Start: stack = [4], result = []. Pop 4, add to result. Push right (6) then left (2).',
|
|
codeLine: 12,
|
|
dataState: {
|
|
arrays: [createOutputState([4, null, null, null, null, null, null], 0)],
|
|
pointers: [],
|
|
variables: [
|
|
{ id: 'node', name: 'node', value: 4 },
|
|
{ id: 'result', name: 'result', value: '[4]' },
|
|
],
|
|
calculations: [],
|
|
trees: [createTreeState({ [NODE_4]: 'visited', [NODE_2]: 'visiting', [NODE_6]: 'visiting' })],
|
|
stacks: [createStackState([6, 2], true)],
|
|
},
|
|
},
|
|
{
|
|
id: 'exec-2',
|
|
phase: 'execution',
|
|
explanation:
|
|
'Stack: [6, 2]. Pop 2 (top). Result: [4, 2]. Push right (3) then left (1). Stack: [6, 3, 1].',
|
|
codeLine: 12,
|
|
dataState: {
|
|
arrays: [createOutputState([4, 2, null, null, null, null, null], 1)],
|
|
pointers: [],
|
|
variables: [
|
|
{ id: 'node', name: 'node', value: 2, previousValue: 4 },
|
|
{ id: 'result', name: 'result', value: '[4, 2]' },
|
|
],
|
|
calculations: [],
|
|
trees: [createTreeState({ [NODE_4]: 'visited', [NODE_2]: 'visited', [NODE_1]: 'visiting', [NODE_3]: 'visiting', [NODE_6]: 'visiting' })],
|
|
stacks: [createStackState([6, 3, 1], true)],
|
|
},
|
|
},
|
|
{
|
|
id: 'exec-3',
|
|
phase: 'execution',
|
|
explanation:
|
|
'Stack: [6, 3, 1]. Pop 1 (top). Result: [4, 2, 1]. Node 1 has no children. Stack: [6, 3].',
|
|
codeLine: 12,
|
|
dataState: {
|
|
arrays: [createOutputState([4, 2, 1, null, null, null, null], 2)],
|
|
pointers: [],
|
|
variables: [
|
|
{ id: 'node', name: 'node', value: 1, previousValue: 2 },
|
|
{ id: 'result', name: 'result', value: '[4, 2, 1]' },
|
|
],
|
|
calculations: [],
|
|
trees: [createTreeState({ [NODE_4]: 'visited', [NODE_2]: 'visited', [NODE_1]: 'visited', [NODE_3]: 'visiting', [NODE_6]: 'visiting' })],
|
|
stacks: [createStackState([6, 3], true)],
|
|
},
|
|
},
|
|
{
|
|
id: 'exec-4',
|
|
phase: 'execution',
|
|
explanation:
|
|
'Stack: [6, 3]. Pop 3 (top). Result: [4, 2, 1, 3]. Node 3 has no children. Stack: [6]. Left subtree done!',
|
|
codeLine: 12,
|
|
dataState: {
|
|
arrays: [createOutputState([4, 2, 1, 3, null, null, null], 3)],
|
|
pointers: [],
|
|
variables: [
|
|
{ id: 'node', name: 'node', value: 3, previousValue: 1 },
|
|
{ id: 'result', name: 'result', value: '[4, 2, 1, 3]' },
|
|
],
|
|
calculations: [],
|
|
trees: [createTreeState({ [NODE_4]: 'visited', [NODE_2]: 'visited', [NODE_1]: 'visited', [NODE_3]: 'visited', [NODE_6]: 'visiting' })],
|
|
stacks: [createStackState([6], true)],
|
|
},
|
|
},
|
|
{
|
|
id: 'exec-5',
|
|
phase: 'execution',
|
|
explanation:
|
|
'Stack: [6]. Pop 6 (top). Result: [4, 2, 1, 3, 6]. Push right (7) then left (5). Now exploring right subtree!',
|
|
codeLine: 12,
|
|
dataState: {
|
|
arrays: [createOutputState([4, 2, 1, 3, 6, null, null], 4)],
|
|
pointers: [],
|
|
variables: [
|
|
{ id: 'node', name: 'node', value: 6, previousValue: 3 },
|
|
{ id: 'result', name: 'result', value: '[4, 2, 1, 3, 6]' },
|
|
],
|
|
calculations: [],
|
|
trees: [createTreeState({ [NODE_4]: 'visited', [NODE_2]: 'visited', [NODE_1]: 'visited', [NODE_3]: 'visited', [NODE_6]: 'visited', [NODE_5]: 'visiting', [NODE_7]: 'visiting' })],
|
|
stacks: [createStackState([7, 5], true)],
|
|
},
|
|
},
|
|
{
|
|
id: 'exec-6',
|
|
phase: 'execution',
|
|
explanation:
|
|
'Stack: [7, 5]. Pop 5 (top). Result: [4, 2, 1, 3, 6, 5]. Node 5 has no children. Stack: [7].',
|
|
codeLine: 12,
|
|
dataState: {
|
|
arrays: [createOutputState([4, 2, 1, 3, 6, 5, null], 5)],
|
|
pointers: [],
|
|
variables: [
|
|
{ id: 'node', name: 'node', value: 5, previousValue: 6 },
|
|
{ id: 'result', name: 'result', value: '[4, 2, 1, 3, 6, 5]' },
|
|
],
|
|
calculations: [],
|
|
trees: [createTreeState({ [NODE_4]: 'visited', [NODE_2]: 'visited', [NODE_1]: 'visited', [NODE_3]: 'visited', [NODE_6]: 'visited', [NODE_5]: 'visited', [NODE_7]: 'visiting' })],
|
|
stacks: [createStackState([7], true)],
|
|
},
|
|
},
|
|
{
|
|
id: 'exec-7',
|
|
phase: 'execution',
|
|
explanation:
|
|
'Stack: [7]. Pop 7 (top). Result: [4, 2, 1, 3, 6, 5, 7]. Node 7 has no children. Stack: [].',
|
|
codeLine: 12,
|
|
dataState: {
|
|
arrays: [createOutputState([4, 2, 1, 3, 6, 5, 7], 6)],
|
|
pointers: [],
|
|
variables: [
|
|
{ id: 'node', name: 'node', value: 7, previousValue: 5 },
|
|
{ id: 'result', name: 'result', value: '[4, 2, 1, 3, 6, 5, 7]' },
|
|
],
|
|
calculations: [],
|
|
trees: [createTreeState({ [NODE_4]: 'visited', [NODE_2]: 'visited', [NODE_1]: 'visited', [NODE_3]: 'visited', [NODE_6]: 'visited', [NODE_5]: 'visited', [NODE_7]: 'visited' })],
|
|
stacks: [createStackState([])],
|
|
},
|
|
},
|
|
{
|
|
id: 'exec-8',
|
|
phase: 'execution',
|
|
explanation:
|
|
'Stack is empty! All nodes have been visited. Final result: [4, 2, 1, 3, 6, 5, 7] - preorder DFS traversal.',
|
|
codeLine: 20,
|
|
dataState: {
|
|
arrays: [createOutputState([4, 2, 1, 3, 6, 5, 7])],
|
|
pointers: [],
|
|
variables: [
|
|
{ id: 'result', name: 'result', value: '[4, 2, 1, 3, 6, 5, 7]' },
|
|
],
|
|
calculations: [],
|
|
trees: [createTreeState({ [NODE_4]: 'visited', [NODE_2]: 'visited', [NODE_1]: 'visited', [NODE_3]: 'visited', [NODE_6]: 'visited', [NODE_5]: 'visited', [NODE_7]: 'visited' })],
|
|
stacks: [createStackState([])],
|
|
},
|
|
},
|
|
],
|
|
};
|