|
| 1 | +# Design Document |
| 2 | + |
| 3 | +## Overview |
| 4 | + |
| 5 | +本功能为 LeetCode 199「二叉树的右视图」算法可视化项目添加双解法支持。用户可以从导航栏选择 DFS(深度优先搜索)或 BFS(广度优先搜索/层序遍历)两种解法,每种解法有独立的画布和可视化界面。BFS 解法在画布上展示队列数据结构的可视化,帮助用户理解层序遍历的工作原理。 |
| 6 | + |
| 7 | +## Architecture |
| 8 | + |
| 9 | +### 整体架构变更 |
| 10 | + |
| 11 | +``` |
| 12 | +┌─────────────────────────────────────────────────────────────┐ |
| 13 | +│ App (主容器) │ |
| 14 | +├─────────────────────────────────────────────────────────────┤ |
| 15 | +│ Header (标题栏) │ |
| 16 | +├─────────────────────────────────────────────────────────────┤ |
| 17 | +│ AlgorithmSelector (算法选择器) [新增] │ |
| 18 | +│ ├── DFSButton (DFS 解法按钮) │ |
| 19 | +│ └── BFSButton (BFS 解法按钮) │ |
| 20 | +├─────────────────────────────────────────────────────────────┤ |
| 21 | +│ DataInput (数据输入区) [共享] │ |
| 22 | +├─────────────────────────────────────────────────────────────┤ |
| 23 | +│ MainContent (主内容区) │ |
| 24 | +│ ├── DFSView [条件渲染] │ BFSView [条件渲染] │ |
| 25 | +│ │ ├── DFSCodePanel │ ├── BFSCodePanel │ |
| 26 | +│ │ └── DFSCanvas │ └── BFSCanvas │ |
| 27 | +│ │ │ └── QueueVisualization│ |
| 28 | +├─────────────────────────────────────────────────────────────┤ |
| 29 | +│ PlaybackControls (播放控制) [共享,状态独立] │ |
| 30 | +└─────────────────────────────────────────────────────────────┘ |
| 31 | +``` |
| 32 | + |
| 33 | +### 状态管理架构 |
| 34 | + |
| 35 | +``` |
| 36 | +AppState |
| 37 | +├── inputValue: string // 共享输入数据 |
| 38 | +├── tree: TreeNode | null // 共享二叉树 |
| 39 | +├── currentAlgorithm: 'dfs' | 'bfs' // 当前选中算法 |
| 40 | +├── dfsState // DFS 视图状态 |
| 41 | +│ ├── steps: AnimationStep[] |
| 42 | +│ ├── currentStepIndex: number |
| 43 | +│ └── isPlaying: boolean |
| 44 | +└── bfsState // BFS 视图状态 |
| 45 | + ├── steps: BFSAnimationStep[] |
| 46 | + ├── currentStepIndex: number |
| 47 | + └── isPlaying: boolean |
| 48 | +``` |
| 49 | + |
| 50 | +## Components and Interfaces |
| 51 | + |
| 52 | +### 新增组件 |
| 53 | + |
| 54 | +#### 1. AlgorithmSelector (src/components/AlgorithmSelector/) |
| 55 | +算法选择器组件,用于切换 DFS 和 BFS 视图。 |
| 56 | + |
| 57 | +```typescript |
| 58 | +interface AlgorithmSelectorProps { |
| 59 | + currentAlgorithm: 'dfs' | 'bfs'; |
| 60 | + onAlgorithmChange: (algorithm: 'dfs' | 'bfs') => void; |
| 61 | +} |
| 62 | +``` |
| 63 | + |
| 64 | +#### 2. BFSCodePanel (src/components/BFSCodePanel/) |
| 65 | +BFS 代码面板组件,展示 BFS 算法代码。 |
| 66 | + |
| 67 | +```typescript |
| 68 | +interface BFSCodePanelProps { |
| 69 | + highlightLines: number[]; |
| 70 | + variables: Record<string, unknown>; |
| 71 | + queueState: QueueItem[]; |
| 72 | +} |
| 73 | +``` |
| 74 | + |
| 75 | +#### 3. QueueVisualization (src/components/QueueVisualization/) |
| 76 | +队列可视化组件,在画布下方展示队列状态。 |
| 77 | + |
| 78 | +```typescript |
| 79 | +interface QueueVisualizationProps { |
| 80 | + queue: QueueItem[]; |
| 81 | + highlightedNodeId?: string; |
| 82 | + onDequeue?: () => void; |
| 83 | + onEnqueue?: (item: QueueItem) => void; |
| 84 | +} |
| 85 | + |
| 86 | +interface QueueItem { |
| 87 | + nodeId: string; |
| 88 | + value: number; |
| 89 | + isHighlighted?: boolean; |
| 90 | +} |
| 91 | +``` |
| 92 | + |
| 93 | +### 修改组件 |
| 94 | + |
| 95 | +#### App (src/App.tsx) |
| 96 | +扩展状态管理,支持双视图状态独立。 |
| 97 | + |
| 98 | +```typescript |
| 99 | +interface AppState { |
| 100 | + inputValue: string; |
| 101 | + tree: TreeNode | null; |
| 102 | + currentAlgorithm: 'dfs' | 'bfs'; |
| 103 | + language: Language; |
| 104 | + dfsState: { |
| 105 | + steps: AnimationStep[]; |
| 106 | + currentStepIndex: number; |
| 107 | + isPlaying: boolean; |
| 108 | + }; |
| 109 | + bfsState: { |
| 110 | + steps: BFSAnimationStep[]; |
| 111 | + currentStepIndex: number; |
| 112 | + isPlaying: boolean; |
| 113 | + }; |
| 114 | +} |
| 115 | +``` |
| 116 | + |
| 117 | +## Data Models |
| 118 | + |
| 119 | +### BFSAnimationStep (BFS 动画步骤) |
| 120 | + |
| 121 | +```typescript |
| 122 | +interface BFSAnimationStep { |
| 123 | + type: 'init' | 'dequeue' | 'enqueue-left' | 'enqueue-right' | 'record-result' | 'level-complete' | 'return'; |
| 124 | + nodeId?: string; // 当前操作的节点 ID |
| 125 | + level?: number; // 当前层号 |
| 126 | + description: string; // 步骤描述 |
| 127 | + highlightLines: { // 各语言高亮行号 |
| 128 | + java: number[]; |
| 129 | + python: number[]; |
| 130 | + golang: number[]; |
| 131 | + javascript: number[]; |
| 132 | + }; |
| 133 | + variables: Record<string, unknown>; // 变量状态 |
| 134 | + resultSoFar: number[]; // 当前结果数组 |
| 135 | + queueState: QueueItem[]; // 当前队列状态 |
| 136 | + nodeStates: Map<string, NodeState>; // 节点状态映射 |
| 137 | + annotations?: Annotation[]; // 画布标注 |
| 138 | +} |
| 139 | + |
| 140 | +type NodeState = 'unvisited' | 'in-queue' | 'processing' | 'completed' | 'result'; |
| 141 | +``` |
| 142 | + |
| 143 | +### QueueItem (队列项) |
| 144 | + |
| 145 | +```typescript |
| 146 | +interface QueueItem { |
| 147 | + nodeId: string; // 节点 ID |
| 148 | + value: number; // 节点值 |
| 149 | + level: number; // 所在层级 |
| 150 | +} |
| 151 | +``` |
| 152 | + |
| 153 | +### BFS 代码片段 |
| 154 | + |
| 155 | +```typescript |
| 156 | +// src/constants/bfsCodeSnippets.ts |
| 157 | +export const bfsCodeSnippets = { |
| 158 | + java: `class Solution { |
| 159 | + public List<Integer> rightSideView(TreeNode root) { |
| 160 | + List<Integer> ans = new ArrayList<>(); |
| 161 | + if (root == null) return ans; |
| 162 | + |
| 163 | + Queue<TreeNode> queue = new LinkedList<>(); |
| 164 | + queue.offer(root); |
| 165 | + |
| 166 | + while (!queue.isEmpty()) { |
| 167 | + int levelSize = queue.size(); |
| 168 | + for (int i = 0; i < levelSize; i++) { |
| 169 | + TreeNode node = queue.poll(); |
| 170 | + if (i == levelSize - 1) { |
| 171 | + ans.add(node.val); |
| 172 | + } |
| 173 | + if (node.left != null) queue.offer(node.left); |
| 174 | + if (node.right != null) queue.offer(node.right); |
| 175 | + } |
| 176 | + } |
| 177 | + return ans; |
| 178 | + } |
| 179 | +}`, |
| 180 | + // ... python, golang, javascript |
| 181 | +}; |
| 182 | +``` |
| 183 | + |
| 184 | +## Correctness Properties |
| 185 | + |
| 186 | +*A property is a characteristic or behavior that should hold true across all valid executions of a system-essentially, a formal statement about what the system should do. Properties serve as the bridge between human-readable specifications and machine-verifiable correctness guarantees.* |
| 187 | + |
| 188 | +### Property 1: 输入数据跨视图同步 |
| 189 | +*For any* 输入数据修改操作,无论在 DFS 还是 BFS 视图中进行,切换到另一个视图后输入数据应保持一致。 |
| 190 | +**Validates: Requirements 1.5, 6.3** |
| 191 | + |
| 192 | +### Property 2: 视图切换重置播放状态 |
| 193 | +*For any* 视图切换操作,切换后当前视图的动画步骤索引应重置为 0。 |
| 194 | +**Validates: Requirements 1.6, 6.4** |
| 195 | + |
| 196 | +### Property 3: BFS 动画步骤包含队列状态 |
| 197 | +*For any* BFS 动画步骤,该步骤的 queueState 字段应包含当前队列中所有节点的信息。 |
| 198 | +**Validates: Requirements 3.2** |
| 199 | + |
| 200 | +### Property 4: BFS 队列操作步骤标记正确性 |
| 201 | +*For any* BFS 动画中的入队或出队操作,步骤类型应正确标记为 'enqueue-left'、'enqueue-right' 或 'dequeue',且包含对应节点信息。 |
| 202 | +**Validates: Requirements 3.4, 3.5** |
| 203 | + |
| 204 | +### Property 5: BFS 右视图结果正确性 |
| 205 | +*For any* 非空二叉树,BFS 算法生成的右视图结果应等于每层最后一个节点值的集合。 |
| 206 | +**Validates: Requirements 3.6** |
| 207 | + |
| 208 | +### Property 6: BFS 代码高亮行有效性 |
| 209 | +*For any* BFS 动画步骤和任意支持的语言,该步骤的 highlightLines 应包含该语言对应的有效行号数组,且行号在 BFS 代码范围内。 |
| 210 | +**Validates: Requirements 2.2** |
| 211 | + |
| 212 | +### Property 7: 队列与树节点高亮同步 |
| 213 | +*For any* 被高亮的树节点,如果该节点在队列中,则队列可视化中对应的方块应同步高亮。 |
| 214 | +**Validates: Requirements 4.7** |
| 215 | + |
| 216 | +### Property 8: BFS 节点状态转换正确性 |
| 217 | +*For any* BFS 动画执行过程中的节点,其状态应按照 unvisited → in-queue → processing → completed/result 的顺序转换,不应跳过中间状态。 |
| 218 | +**Validates: Requirements 5.2, 5.3, 5.4, 5.5** |
| 219 | + |
| 220 | +### Property 9: 视图状态往返恢复 |
| 221 | +*For any* 视图切换序列(如 DFS → BFS → DFS),返回原视图后播放进度应恢复到切换前的状态。 |
| 222 | +**Validates: Requirements 6.1, 6.2** |
| 223 | + |
| 224 | +## Error Handling |
| 225 | + |
| 226 | +### 视图切换错误处理 |
| 227 | +- 切换时动画正在播放:自动暂停当前动画 |
| 228 | +- 切换时数据无效:保持在当前视图并显示错误提示 |
| 229 | + |
| 230 | +### 队列可视化错误处理 |
| 231 | +- 队列为空:显示空队列占位符 |
| 232 | +- 队列过长超出显示区域:启用水平滚动或压缩显示 |
| 233 | + |
| 234 | +### 状态同步错误处理 |
| 235 | +- 状态不一致:以最新输入数据为准重新生成动画步骤 |
| 236 | + |
| 237 | +## Testing Strategy |
| 238 | + |
| 239 | +### 单元测试 |
| 240 | +使用 Vitest 进行单元测试: |
| 241 | +- BFS 动画步骤生成函数测试 |
| 242 | +- 队列状态管理函数测试 |
| 243 | +- 视图状态切换函数测试 |
| 244 | + |
| 245 | +### 属性测试 |
| 246 | +使用 fast-check 进行属性测试: |
| 247 | +- 每个属性测试运行至少 100 次迭代 |
| 248 | +- 测试文件使用 `*.property.test.ts` 命名 |
| 249 | +- 每个测试用注释标注对应的 Property 编号和 Requirements |
| 250 | + |
| 251 | +**属性测试标注格式:** |
| 252 | +```typescript |
| 253 | +// **Feature: dual-algorithm-views, Property 1: 输入数据跨视图同步** |
| 254 | +// **Validates: Requirements 1.5, 6.3** |
| 255 | +``` |
| 256 | + |
| 257 | +### 测试文件结构 |
| 258 | +``` |
| 259 | +src/ |
| 260 | +├── utils/ |
| 261 | +│ ├── bfsAlgorithm.ts |
| 262 | +│ ├── bfsAlgorithm.property.test.ts # BFS 属性测试 |
| 263 | +│ └── viewState.property.test.ts # 视图状态属性测试 |
| 264 | +├── components/ |
| 265 | +│ ├── QueueVisualization/ |
| 266 | +│ │ └── QueueVisualization.test.tsx |
| 267 | +│ └── AlgorithmSelector/ |
| 268 | +│ └── AlgorithmSelector.test.tsx |
| 269 | +``` |
| 270 | + |
0 commit comments