|
| 1 | +# Design Document: Algorithm Visualization Platform |
| 2 | + |
| 3 | +## Overview |
| 4 | + |
| 5 | +本设计文档描述一个以教学为目的的 LeetCode 算法可视化演示平台。平台使用 TypeScript + React + D3.js 技术栈,以单屏幕形式展示算法的分步骤执行过程。核心目标是通过交互式画布、代码同步高亮、数据流可视化等功能,帮助用户直观理解算法执行过程。 |
| 6 | + |
| 7 | +## Architecture |
| 8 | + |
| 9 | +### 整体架构 |
| 10 | + |
| 11 | +``` |
| 12 | +┌─────────────────────────────────────────────────────────────────┐ |
| 13 | +│ Header │ |
| 14 | +│ [GitHub ⭐] [101. 对称二叉树 →] │ |
| 15 | +├─────────────────────────────────────────────────────────────────┤ |
| 16 | +│ [输入框] [样例1] [样例2] [样例3] [🎲随机] │ |
| 17 | +├─────────────────────────────────────────────────────────────────┤ |
| 18 | +│ │ |
| 19 | +│ ┌─────────────────────────────┐ ┌──────────────────────────┐ │ |
| 20 | +│ │ │ │ Code Display │ │ |
| 21 | +│ │ Canvas (D3.js) │ │ ┌────────────────────┐ │ │ |
| 22 | +│ │ │ │ │ Java Python Go JS │ │ │ |
| 23 | +│ │ [Tree Visualization] │ │ ├────────────────────┤ │ │ |
| 24 | +│ │ [Data Flow Arrows] │ │ │ 1│ public boolean │ │ │ |
| 25 | +│ │ [Annotations] │ │ │►2│ if (root == │ │ │ |
| 26 | +│ │ [Recursion Stack] │ │ │ 3│ return isMir │ │ │ |
| 27 | +│ │ │ │ │ │ left=1 ✓ │ │ │ |
| 28 | +│ │ │ │ └────────────────────┘ │ │ |
| 29 | +│ └─────────────────────────────┘ └──────────────────────────┘ │ |
| 30 | +│ │ |
| 31 | +├─────────────────────────────────────────────────────────────────┤ |
| 32 | +│ [⏮ R] [⏪ ←] [▶ 空格] [→ ⏩] [⏭] 速度: [0.5x][1x][2x][4x] │ |
| 33 | +│ ████████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ │ |
| 34 | +└─────────────────────────────────────────────────────────────────┘ |
| 35 | + [💬 交流群] |
| 36 | +``` |
| 37 | + |
| 38 | +### 组件层次结构 |
| 39 | + |
| 40 | +``` |
| 41 | +App |
| 42 | +├── Header |
| 43 | +│ ├── GitHubBadge (Star count with IndexedDB cache) |
| 44 | +│ └── LeetCodeTitle (Clickable link) |
| 45 | +├── DataInputBar |
| 46 | +│ ├── TextInput |
| 47 | +│ ├── PresetExamples |
| 48 | +│ └── RandomGenerator |
| 49 | +├── MainContent |
| 50 | +│ ├── CanvasPanel |
| 51 | +│ │ ├── TreeVisualization (D3.js) |
| 52 | +│ │ ├── DataFlowArrows |
| 53 | +│ │ ├── AnnotationSystem |
| 54 | +│ │ └── RecursionStackOverlay |
| 55 | +│ └── CodePanel |
| 56 | +│ ├── LanguageTabs |
| 57 | +│ ├── CodeLines (with line numbers) |
| 58 | +│ ├── ExecutionHighlight |
| 59 | +│ └── VariableWatch |
| 60 | +├── BottomControlBar |
| 61 | +│ ├── PlaybackControls |
| 62 | +│ ├── SpeedSelector |
| 63 | +│ └── ProgressBar |
| 64 | +└── WeChatFloat |
| 65 | +``` |
| 66 | + |
| 67 | +## Components and Interfaces |
| 68 | + |
| 69 | +### 1. Header 组件 |
| 70 | + |
| 71 | +```typescript |
| 72 | +interface HeaderProps { |
| 73 | + title: string; // "101. 对称二叉树" |
| 74 | + leetcodeUrl: string; // LeetCode 题目链接 |
| 75 | + githubUrl: string; // GitHub 仓库链接 |
| 76 | + githubRepo: string; // "owner/repo" 格式 |
| 77 | +} |
| 78 | + |
| 79 | +interface GitHubStarCache { |
| 80 | + stars: number; |
| 81 | + timestamp: number; // 缓存时间戳 |
| 82 | +} |
| 83 | +``` |
| 84 | + |
| 85 | +### 2. DataInputBar 组件 |
| 86 | + |
| 87 | +```typescript |
| 88 | +interface DataInputBarProps { |
| 89 | + onDataChange: (data: TreeData) => void; |
| 90 | + onError: (error: string) => void; |
| 91 | +} |
| 92 | + |
| 93 | +interface PresetExample { |
| 94 | + label: string; |
| 95 | + value: string; |
| 96 | + description?: string; |
| 97 | +} |
| 98 | + |
| 99 | +// 数据验证函数 |
| 100 | +function validateTreeInput(input: string): ValidationResult; |
| 101 | +function generateRandomTree(constraints: TreeConstraints): TreeData; |
| 102 | +``` |
| 103 | + |
| 104 | +### 3. CodeDisplay 组件 |
| 105 | + |
| 106 | +```typescript |
| 107 | +type Language = 'java' | 'python' | 'golang' | 'javascript'; |
| 108 | + |
| 109 | +interface CodeDisplayProps { |
| 110 | + method: 'recursive' | 'iterative'; |
| 111 | + currentStep: number; |
| 112 | + variables: VariableMap; |
| 113 | +} |
| 114 | + |
| 115 | +interface CodeLine { |
| 116 | + lineNumber: number; |
| 117 | + content: string; |
| 118 | + isHighlighted: boolean; |
| 119 | + isExecuting: boolean; |
| 120 | + variables?: VariableAnnotation[]; |
| 121 | +} |
| 122 | + |
| 123 | +interface VariableAnnotation { |
| 124 | + name: string; |
| 125 | + value: string | number | boolean | null; |
| 126 | + type: 'node' | 'value' | 'result' | 'size'; |
| 127 | +} |
| 128 | +``` |
| 129 | + |
| 130 | +### 4. Canvas 组件 |
| 131 | + |
| 132 | +```typescript |
| 133 | +interface CanvasProps { |
| 134 | + root: TreeNode | null; |
| 135 | + currentStep: AlgorithmStep; |
| 136 | + animationEnabled: boolean; |
| 137 | +} |
| 138 | + |
| 139 | +interface DataFlowArrow { |
| 140 | + from: Position; |
| 141 | + to: Position; |
| 142 | + label: string; |
| 143 | + type: 'value-transfer' | 'comparison' | 'return'; |
| 144 | +} |
| 145 | + |
| 146 | +interface Annotation { |
| 147 | + nodeId: string; |
| 148 | + text: string; |
| 149 | + position: 'top' | 'bottom' | 'left' | 'right'; |
| 150 | + type: 'compare' | 'result' | 'info'; |
| 151 | +} |
| 152 | +``` |
| 153 | + |
| 154 | +### 5. BottomControlBar 组件 |
| 155 | + |
| 156 | +```typescript |
| 157 | +interface BottomControlBarProps { |
| 158 | + currentStep: number; |
| 159 | + totalSteps: number; |
| 160 | + isPlaying: boolean; |
| 161 | + playSpeed: number; |
| 162 | + onStepChange: (step: number) => void; |
| 163 | + onPlayToggle: () => void; |
| 164 | + onSpeedChange: (speed: number) => void; |
| 165 | + onReset: () => void; |
| 166 | +} |
| 167 | + |
| 168 | +// IndexedDB 存储接口 |
| 169 | +interface PlaybackSettings { |
| 170 | + speed: number; |
| 171 | + lastUpdated: number; |
| 172 | +} |
| 173 | +``` |
| 174 | + |
| 175 | +### 6. WeChatFloat 组件 |
| 176 | + |
| 177 | +```typescript |
| 178 | +interface WeChatFloatProps { |
| 179 | + qrCodeUrl: string; |
| 180 | + instructions: string; |
| 181 | +} |
| 182 | +``` |
| 183 | + |
| 184 | +## Data Models |
| 185 | + |
| 186 | +### TreeNode |
| 187 | + |
| 188 | +```typescript |
| 189 | +interface TreeNode { |
| 190 | + val: number; |
| 191 | + left: TreeNode | null; |
| 192 | + right: TreeNode | null; |
| 193 | +} |
| 194 | +``` |
| 195 | + |
| 196 | +### AlgorithmStep |
| 197 | + |
| 198 | +```typescript |
| 199 | +interface AlgorithmStep { |
| 200 | + stepIndex: number; |
| 201 | + type: 'initial' | 'compare' | 'check-null' | 'check-value' | 'check-subtree' | 'return' | 'final'; |
| 202 | + message: string; |
| 203 | + detailedMessage?: string; |
| 204 | + leftNodeId?: string; |
| 205 | + rightNodeId?: string; |
| 206 | + result?: boolean; |
| 207 | + codeLineNumber: number; |
| 208 | + variables: VariableMap; |
| 209 | + annotations: Annotation[]; |
| 210 | + dataFlowArrows: DataFlowArrow[]; |
| 211 | +} |
| 212 | +``` |
| 213 | + |
| 214 | +### IndexedDB Schema |
| 215 | + |
| 216 | +```typescript |
| 217 | +// Database: leetcode-symmetric-tree-db |
| 218 | +// Stores: |
| 219 | +// 1. github-cache: { key: string, stars: number, timestamp: number } |
| 220 | +// 2. settings: { key: string, value: any } |
| 221 | +``` |
| 222 | + |
| 223 | +## Correctness Properties |
| 224 | + |
| 225 | +*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.* |
| 226 | + |
| 227 | +### Property 1: GitHub Star Cache Validity |
| 228 | +*For any* star count fetched from the GitHub API, the system SHALL store it in IndexedDB with a timestamp, and subsequent fetches within one hour SHALL return the cached value without making an API call. |
| 229 | +**Validates: Requirements 2.4** |
| 230 | + |
| 231 | +### Property 2: Random Tree Generation Validity |
| 232 | +*For any* randomly generated tree data, the output SHALL conform to the problem constraints (valid array format, root not null, all values are numbers or null). |
| 233 | +**Validates: Requirements 3.6** |
| 234 | + |
| 235 | +### Property 3: Input Validation Correctness |
| 236 | +*For any* user input string, the validation function SHALL correctly identify whether it represents a valid tree array according to the problem constraints. |
| 237 | +**Validates: Requirements 3.7** |
| 238 | + |
| 239 | +### Property 4: Code-Step Synchronization |
| 240 | +*For any* algorithm step, the code display SHALL highlight the corresponding code line that matches the step's execution point. |
| 241 | +**Validates: Requirements 4.3** |
| 242 | + |
| 243 | +### Property 5: Variable Display Accuracy |
| 244 | +*For any* variable value change during algorithm execution, the displayed value SHALL match the actual value at that step. |
| 245 | +**Validates: Requirements 4.4** |
| 246 | + |
| 247 | +### Property 6: Keyboard Shortcut Consistency |
| 248 | +*For any* keyboard shortcut press (←, →, Space, R), the system SHALL execute the corresponding action (previous step, next step, play/pause, reset). |
| 249 | +**Validates: Requirements 7.5, 7.6, 7.7, 7.8** |
| 250 | + |
| 251 | +### Property 7: Playback Speed Persistence |
| 252 | +*For any* playback speed change, the new speed SHALL be persisted to IndexedDB and restored on page reload. |
| 253 | +**Validates: Requirements 7.10** |
| 254 | + |
| 255 | +### Property 8: No Purple Colors |
| 256 | +*For any* rendered element on the page, its color values SHALL NOT contain purple hues (hue values between 270-330 degrees in HSL color space). |
| 257 | +**Validates: Requirements 10.2** |
| 258 | + |
| 259 | +### Property 9: Comparison Visualization |
| 260 | +*For any* comparison step in the algorithm, the canvas SHALL display visual indicators (arrows, highlights, labels) showing the two values being compared. |
| 261 | +**Validates: Requirements 6.4** |
| 262 | + |
| 263 | +### Property 10: Auto-fit Large Trees |
| 264 | +*For any* tree with more than 7 nodes, the canvas SHALL automatically adjust the zoom level so that all nodes are visible within the viewport. |
| 265 | +**Validates: Requirements 5.4** |
| 266 | + |
| 267 | +## Error Handling |
| 268 | + |
| 269 | +1. **GitHub API 失败**: 使用 IndexedDB 缓存的值,如果无缓存则显示 0 |
| 270 | +2. **输入验证失败**: 显示具体错误信息,阻止可视化 |
| 271 | +3. **随机生成失败**: 显示错误提示,允许重试 |
| 272 | +4. **IndexedDB 不可用**: 降级为内存存储,功能正常但不持久化 |
| 273 | +5. **D3.js 渲染错误**: 显示错误边界,提供重置选项 |
| 274 | + |
| 275 | +## Testing Strategy |
| 276 | + |
| 277 | +### 单元测试 |
| 278 | + |
| 279 | +使用 Vitest 进行单元测试: |
| 280 | + |
| 281 | +1. 测试 `validateTreeInput` 函数正确验证各种输入 |
| 282 | +2. 测试 `generateRandomTree` 函数生成有效数据 |
| 283 | +3. 测试 `getCodeLineForStep` 函数返回正确的行号 |
| 284 | +4. 测试 IndexedDB 缓存读写功能 |
| 285 | +5. 测试颜色验证函数排除紫色 |
| 286 | + |
| 287 | +### 属性测试 |
| 288 | + |
| 289 | +使用 fast-check 进行属性测试: |
| 290 | + |
| 291 | +1. **Property 1 测试**: 生成随机 star 数和时间戳,验证缓存行为 |
| 292 | +2. **Property 2 测试**: 多次调用随机生成,验证所有输出都有效 |
| 293 | +3. **Property 3 测试**: 生成随机字符串输入,验证验证函数正确分类 |
| 294 | +4. **Property 4 测试**: 生成随机步骤序列,验证代码行高亮正确 |
| 295 | +5. **Property 5 测试**: 生成随机变量值,验证显示值匹配 |
| 296 | +6. **Property 6 测试**: 模拟随机键盘事件,验证动作正确 |
| 297 | +7. **Property 7 测试**: 生成随机速度值,验证持久化和恢复 |
| 298 | +8. **Property 8 测试**: 检查所有 CSS 颜色值,验证无紫色 |
| 299 | +9. **Property 9 测试**: 生成随机比较步骤,验证可视化元素存在 |
| 300 | +10. **Property 10 测试**: 生成不同大小的树,验证自动缩放 |
| 301 | + |
| 302 | +每个属性测试配置运行至少 100 次迭代。 |
| 303 | + |
| 304 | +测试文件命名规范:`*.test.ts` |
| 305 | + |
| 306 | +测试标注格式: |
| 307 | +```typescript |
| 308 | +// **Feature: algorithm-visualization-platform, Property 1: GitHub Star Cache Validity** |
| 309 | +``` |
| 310 | + |
| 311 | +## 技术实现细节 |
| 312 | + |
| 313 | +### IndexedDB 缓存策略 |
| 314 | + |
| 315 | +```typescript |
| 316 | +const CACHE_DURATION = 60 * 60 * 1000; // 1小时 |
| 317 | + |
| 318 | +async function getStarCount(repo: string): Promise<number> { |
| 319 | + const cached = await getCachedStars(); |
| 320 | + if (cached && Date.now() - cached.timestamp < CACHE_DURATION) { |
| 321 | + return cached.stars; |
| 322 | + } |
| 323 | + try { |
| 324 | + const stars = await fetchFromGitHub(repo); |
| 325 | + await setCachedStars(stars); |
| 326 | + return stars; |
| 327 | + } catch { |
| 328 | + return cached?.stars ?? 0; |
| 329 | + } |
| 330 | +} |
| 331 | +``` |
| 332 | + |
| 333 | +### 代码-步骤同步映射 |
| 334 | + |
| 335 | +```typescript |
| 336 | +// 每种语言、每种方法的步骤类型到代码行的映射 |
| 337 | +const stepToLineMap: Record<Language, Record<Method, Record<StepType, number>>> = { |
| 338 | + java: { |
| 339 | + recursive: { |
| 340 | + 'initial': 1, |
| 341 | + 'check-null': 6, |
| 342 | + 'check-value': 8, |
| 343 | + 'compare': 9, |
| 344 | + 'check-subtree': 10, |
| 345 | + 'return': 10, |
| 346 | + 'final': 3 |
| 347 | + }, |
| 348 | + // ... 其他方法 |
| 349 | + }, |
| 350 | + // ... 其他语言 |
| 351 | +}; |
| 352 | +``` |
| 353 | + |
| 354 | +### 颜色验证 |
| 355 | + |
| 356 | +```typescript |
| 357 | +function isPurple(color: string): boolean { |
| 358 | + const hsl = colorToHSL(color); |
| 359 | + return hsl.h >= 270 && hsl.h <= 330; |
| 360 | +} |
| 361 | + |
| 362 | +function validateNoP urple(element: HTMLElement): boolean { |
| 363 | + const styles = getComputedStyle(element); |
| 364 | + const colorProps = ['color', 'backgroundColor', 'borderColor', 'fill', 'stroke']; |
| 365 | + return colorProps.every(prop => !isPurple(styles[prop])); |
| 366 | +} |
| 367 | +``` |
0 commit comments