docs/current/05-教学体验/算法与数据结构教学设计.md
GitHub ↗
当前有效

算法修复、子集扩展与数据结构支持设计

8388 字·约 21 分钟 阅读 2026-10-10 17:07

核心问题:

  1. 算法层面的错误如何修复?
  2. 如何逐步扩展 C 子集?
  3. 如何支持数据结构教学?

最后核对日期:2026-10-04(S8 收官回灌——算法识别/推断已随 teaching/steps 落地:43 族判据+43 推断 + 311/311 标注 golden 对拍 + compile 帧 algorithm_matches wire 出口,见S8 总览;§7 G9 缺口叙述保留为历史口径。前一沿革 2026-09-29 全库逐份翻新复核) 阶段完成状态:Phase 1 / Phase 2 / Phase 3 已完成、Phase 4 部分完成(OCR 项已随前端切割作废,OJ 项保留待做);前端切割后本仓库只做后端,算法验证/可视化能力以"后端能力 + 三出口载荷"形态保留。 修订说明(2026-09-11):去前端化——清除前端桥接实现位置与前端集成段落,改为语言中立后端能力与三出口(capi / wasm32 / vitro_cli serve)载荷;Phase 4 的 OCR 项标注作废;带日期的历史条目保持原样。


1. 算法层面的修复

1.1 算法错误的本质

算法层面的错误与语法/语义错误的根本区别:

错误层级 示例 编译器能否发现? 运行时能否崩溃?
语法错误 inta = 5(缺空格) ✅ 能 ❌ 不运行
语义错误 arr[10](数组越界) ⚠️ 部分能 ✅ 运行时 trap
算法错误 冒泡排序边界 i < n 应为 i < n-1 ❌ 不能 ❌ 不崩溃,但结果错误
算法错误 快排分区逻辑写错 ❌ 不能 ❌ 不崩溃,但结果错误
算法错误 递归缺少终止条件 ⚠️ 部分能 ✅ 栈溢出

核心洞察:算法错误 = 语法正确 + 意图可识别 + 实现有偏差

1.2 三级算法修复策略

学生代码
  ↓
┌─────────────────────────────────────────────┐
│ Level 1: 算法模式识别(AST 层)              │
│ • 将学生代码与已知算法模板进行结构匹配        │
│ • 识别"这是冒泡排序"、"这是二分查找"         │
│ • 对比边界条件、循环变量的差异               │
└─────────────────────────────────────────────┘
  ↓
┌─────────────────────────────────────────────┐
│ Level 2: 运行时验证(Property-based Testing)│
│ • 自动生成测试用例                           │
│ • 调用学生代码 + 验证结果属性                │
│ • 如果不通过,定位可疑代码区域               │
└─────────────────────────────────────────────┘
  ↓
┌─────────────────────────────────────────────┐
│ Level 3: 执行轨迹分析(Trace Analysis)      │
│ • 记录 VitroVM 执行轨迹(比较/交换/递归调用)  │
│ • 与标准轨迹对比                             │
│ • 发现"少比较了一次"、"少交换了一次"等      │
└─────────────────────────────────────────────┘

1.3 Level 1: 算法模式识别

1.3.1 算法模板库

当前系统内置 88 个代码模板(82 个 C + 6 个 C++,见 模板维护指南.md §1;本节首版时为 43 个,C++ 批次后扩至 88,C++ 线已随砍 C++ 裁定冻结),分为 9 大类:

类别 数量 包含模板
排序 8 冒泡排序、选择排序、插入排序、快速排序、归并排序、堆排序、希尔排序、计数排序
查找 2 线性查找、二分查找
图算法 2 BFS 广度优先搜索、DFS 深度优先搜索
动态规划 2 斐波那契数列、01 背包
数据结构 17 顺序表、链表节点/头插/尾插/遍历/删除/双向链表/循环链表、二叉树节点/先序/中序/后序/层序遍历、BST 插入与查找、链栈、链队列、循环队列、哈希表(线性探测)、约瑟夫环
字符串 1 字符串反转
基础 5 数组遍历求和、数组求最大值、指针交换两数、GCD 欧几里得、素数判断
递归 3 阶乘、斐波那契、汉诺塔
指针 1 指针基础操作

模板实现为 templates/<key>/source.c 文件 + meta.yaml 元数据(占位符语法 /*__PARAM_n__*/ 5,见模板维护指南.md §3)。每个模板附带 TutorialStep 列表定义算法执行阶段(outer_loop / compare / swap / partition / merge / enqueue / dequeue / visit / finish 等),以及 LineExplanation 关键行中文解释(TutorialStep/LineExplanation 的 Dart widget 载体已随前端切割迁出,缺口记录见本文 §7)。

1.3.2 AST 结构匹配算法

class AlgorithmMatcher {
public:
    MatchResult Match(const FuncDecl& studentFunc) {
        for (const auto& tmpl : templates) {
            auto similarity = CalculateSimilarity(studentFunc, tmpl.astPattern);
            if (similarity > 0.7f) {  // 相似度阈值
                return {
                    .matched = true,
                    .algorithm = tmpl.name,
                    .similarity = similarity,
                    .deviations = FindDeviations(studentFunc, tmpl)
                };
            }
        }
        return {.matched = false};
    }
    
private:
    float CalculateSimilarity(const FuncDecl& func, const ASTPattern& pattern) {
        // 1. 函数签名匹配(参数类型和数量)
        float signatureScore = MatchSignature(func, pattern);
        
        // 2. 嵌套结构匹配(是否有双重循环)
        float structureScore = MatchStructure(func.body, pattern.body);
        
        // 3. 关键操作匹配(是否有比较、交换)
        float operationScore = MatchOperations(func.body, pattern.body);
        
        return signatureScore * 0.3f + structureScore * 0.4f + operationScore * 0.3f;
    }
    
    std::vector<Deviation> FindDeviations(const FuncDecl& func, const AlgorithmTemplate& tmpl) {
        std::vector<Deviation> result;
        
        // 检查每个常见错误模式
        for (const auto& mistake : tmpl.commonMistakes) {
            if (FindPattern(func.body, mistake.pattern)) {
                result.push_back({
                    .type = mistake.name,
                    .message = mistake.message,
                    .fixSuggestion = mistake.fixSuggestion,
                    .studentCode = ExtractCode(func.body, mistake.pattern),
                    .correctCode = mistake.correct
                });
            }
        }
        
        return result;
    }
};

1.3.3 算法修复的用户界面

┌──────────────────────────────────────────────┐
│ 🤔 算法诊断                               [×]  │
├──────────────────────────────────────────────┤
│                                              │
│ 我识别出你在实现「冒泡排序」。                 │
│                                              │
│ 你的代码(第 3~8 行):                        │
│ ┌──────────────────────────────────────────┐ │
│ │ 3  │ for (int i = 0; i < n; i++) {       │ │
│ │    │            ^^^^^^^                  │ │
│ │    │            │                        │ │
│ │    │            📝 这里可能有问题         │ │
│ │ 4  │     for (int j = 0; j < n; j++) {   │ │
│ │    │                    ^^^^^            │ │
│ │    │                    │                │ │
│ │    │                    📝 这里可能有问题 │ │
│ │ 5  │         if (arr[j] > arr[j+1]) {    │ │
│ │ 6  │             // 交换...              │ │
│ │ 7  │         }                           │ │
│ │ 8  │     }                               │ │
│ │ 9  │ }                                   │ │
│ └──────────────────────────────────────────┘ │
│                                              │
│ 📊 运行时验证结果:                            │
│   测试用例 [5, 3, 8, 1, 2] → 你的结果 [1, 2, 3, 5] │
│   ❌ 元素 8 丢失了!                           │
│                                              │
│ 🔍 问题分析:                                  │
│ ┌──────────────────────────────────────────┐ │
│ │ 问题 1:外层循环边界过大                     │ │
│ │                                        │ │
│ │ 你的写法:for (int i = 0; i < n; i++)   │ │
│ │ 标准写法:for (int i = 0; i < n - 1; i++)│ │
│ │                                        │ │
│ │ 原因:冒泡排序只需 n-1 趟。因为每趟将一   │ │
│ │      个最大元素"冒泡"到正确位置,n-1 趟   │ │
│ │      后只剩最后一个元素,必然有序。        │ │
│ │                                        │ │
│ │ 💡 记忆口诀:外层 n-1,内层 n-i-1         │ │
│ └──────────────────────────────────────────┘ │
│ ┌──────────────────────────────────────────┐ │
│ │ 问题 2:内层循环边界过大,导致越界访问      │ │
│ │                                        │ │
│ │ 你的写法:j < n                         │ │
│ │ 标准写法:j < n - i - 1                 │ │
│ │                                        │ │
│ │ 原因:当 j = n-1 时,arr[j+1] = arr[n]   │ │
│ │      越界了!而且第 i 趟后最后 i 个元素   │ │
│ │      已有序,不需要再比较。                │ │
│ └──────────────────────────────────────────┘ │
│                                              │
│ [📖 查看标准模板]  [🔧 应用修复]              │
│                                              │
│ ⚠️ 注意:算法修复只是建议,建议你理解原因      │
│    后再决定是否应用。                         │
└──────────────────────────────────────────────┘

1.4 Level 2: 运行时验证(⚠️ 实现载体已随前端切割迁出,后端待重建)

1.4.1 Property-based Testing 实现

实现位置(2026-09-11 核对修正):本层的属性用例与判定逻辑原实现于前端载体(Dart 侧算法验证模型:AlgorithmTestCase / AlgorithmValidationResult + 手写用例),已随 2026-09-11 前端切割迁出,属历史资产(见标签 before-frontend-split)。原文所记 Rust 路径 native/src/engine/algorithm_validator.rs 与 validate_algorithm() 在本仓库中不存在(native/src/engine/ 现仅有 compile_pipeline.rs / session_ops.rs / completion/),此处按"诚实记录"更正。

后端现状(本仓库内已核实):

  • 算法检测:native/src/compiler/algorithm_detector/(detect_algorithms,由 native/src/engine/compile_pipeline.rs 写入会话)+ AlgorithmMatch(native/src/session.rs:name / display_name / func_name / confidence / suggestion / line / vis_events);
  • 模板运行正确性回归:native/tests/cases_template_generated/(模板生成用例,与 Clang golden 对照,属防线 1/2)——这是当前可用的替代验证路径;
  • 剩余缺口(诚实记录):替换学生 main() 的测试桩生成、属性判定(长度守恒 / 非递减 / 元素守恒等)与失败用例明细,当前在本仓库没有实现;Level 2 能力随前端迁出,待社区前端或 wasm 出口认领,或按三出口载荷形状在后端重建。

原设计接口(示意,未在本仓库落地):

pub fn validate_algorithm(source: &str, match_info: &AlgorithmMatch) -> ValidationResult {
    let test_cases = generate_test_cases(&match_info.name);
    for tc in &test_cases {
        let result = run_single_test(source, &match_info.func_name, &match_info.name, tc);
        if !result.passed {
            return result;
        }
    }
    ValidationResult::passed(format!("{} 通过了 {} 组测试用例!", match_info.display_name, test_cases.len()))
}

测试桩生成(替换学生的 main()):

int main() {
    int arr[] = {5, 3, 8, 1, 2};
    int n = 5;
    bubbleSort(arr, n);
    for (int i = 0; i < n; i = i + 1) {
        printf("%d ", arr[i]);
    }
    return 0;
}

支持的验证属性:

算法类型 属性 1 属性 2 属性 3
排序(8 种) 输出长度 = 输入长度 非递减 元素守恒
二分查找 返回值为整数 目标存在时返回正确索引 目标不存在时返回 -1
BFS/DFS 访问节点数正确 无重复访问 -
链表操作 链表不断裂 头指针不为 NULL -

1.4.2 出口集成(后端能力 + 三出口载荷)

后端能力(本仓库已核实,语言中立层):

  • 算法匹配与检测结果:native/src/compiler/algorithm_detector/ + AlgorithmMatch(native/src/session.rs),随编译诊断与运行结果一并返回;
  • 模板运行正确性回归:native/tests/cases_template_generated/(Clang golden 对照);
  • 不含属性验证(见 §1.4.1 剩余缺口)——切割前该能力由前端载体提供,已迁出。

三出口载荷(消费方自行渲染,本仓库不再提供前端):

出口 消费方式
capi(C ABI) vitro_compile_json / vitro_run_json 等 JSON 字符串入口;复杂结构过边界一律 JSON(rust-alloc 所有权,vitro_free_string 释放)
wasm32 同一 C ABI 在浏览器/白盒形态下复用(冒烟已实证:零修改构建 + C API 全链路)
vitro_cli serve JSON-lines(NDJSON)会话:compile / run / step.* / payload.get,见 CLI使用手册.md §6

历史记录:切割前算法 Tab 的"🔍 验证算法"按钮与结果 BottomSheet(绿色通过 / 红色失败 + 用例对比)由前端载体实现,已迁出(见标签 before-frontend-split);其判定逻辑属上述剩余缺口的一部分。

1.4.3 关键依赖:func_name 字段

pub struct AlgorithmMatch {
    pub name: String,
    pub func_name: String,
    pub display_name: String,
    pub suggestion: String,
}

1.5 Level 3: 执行轨迹分析

class TraceAnalyzer {
public:
    // 记录每次 __vitro_step 时的状态
    struct TraceEntry {
        int step;
        int line;
        std::map<std::string, int> variables;  // 变量当前值
        std::vector<int> arrayState;           // 数组当前状态
        std::string operation;                 // "compare", "swap", "recurse"
    };
    
    std::vector<TraceEntry> trace;
    
    void Analyze(const std::vector<TraceEntry>& trace, 
                 const AlgorithmTemplate& tmpl) {
        // 分析 1:比较次数是否正确?
        int compareCount = CountOperations(trace, "compare");
        int expectedCompareCount = tmpl.expectedCompareCount(trace[0].arrayState.size());
        if (compareCount != expectedCompareCount) {
            Report("比较次数异常:实际 %d 次,期望 %d 次", 
                   compareCount, expectedCompareCount);
        }
        
        // 分析 2:是否有未比较的相邻元素?
        auto unCompared = FindUncomparedPairs(trace);
        if (!unCompared.empty()) {
            Report("以下相邻元素未被比较:%s", FormatPairs(unCompared));
        }
        
        // 分析 3:递归深度是否合理?
        int maxDepth = MaxRecursionDepth(trace);
        if (maxDepth > trace[0].arrayState.size()) {
            Report("递归深度 %d 超过数组大小 %d,可能存在无限递归", 
                   maxDepth, trace[0].arrayState.size());
        }
    }
};

1.6 算法修复的分级与原则

┌─────────────────────────────────────────────────────────────┐
│                     算法修复分级                             │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  L1 语法/语义修复(全自动)                                  │
│  ├── 数组越界、空指针、类型不匹配                            │
│  └── 编译器直接发现,自动修复                                │
│                                                             │
│  L2 算法模式修复(建议 + 模板对比)                          │
│  ├── 识别算法类型 → 对比标准模板 → 发现边界/逻辑偏差         │
│  └── 展示「你的代码」vs「标准模板」,由学生决定是否修改      │
│                                                             │
│  L3 运行时验证修复(诊断 + 测试用例)                        │
│  ├── 自动生成测试 → 发现结果错误 → 定位可疑区域              │
│  └── 提供测试失败信息和执行轨迹,引导学生自查                │
│                                                             │
│  L4 逻辑漏洞修复(仅提示,不修复)                           │
│  ├── 递归缺少终止条件、算法逻辑根本性错误                    │
│  └── 仅提供教学提示,不自动修改代码(保护思考过程)          │
│                                                             │
└─────────────────────────────────────────────────────────────┘

核心原则:算法修复的目的是教懂学生,不是代写代码。L2/L3/L4 级别永远不自动应用,只提供分析和引导。


2. C 子集扩展路线图

2.1 四级扩展策略

Phase 1: 核心子集(教学入门)
├── 数据类型:int, int*, int[], struct
├── 控制流:if/else, while, for
├── 函数:定义/调用/递归
├── 内存:malloc/free(简化版)
└── 内置:print_int, print_array

Phase 2: 数据结构基础(解锁条件:完成链表练习)
├── + break/continue
├── + sizeof(简化版,固定返回 4)
├── + 字符串字面量("hello",仅用于输出)
└── + vis_* 可视化内置函数

Phase 3: 进阶语法(解锁条件:完成树/图练习)
├── + 多维数组(int arr[3][4])
├── + typedef(struct 别名)
├── + 枚举 enum
└── + 函数指针(简化版,用于 qsort)

Phase 4: 实用编程(解锁条件:完成综合项目)
├── + 字符串操作(strlen, strcpy, strcmp 简化版)
├── + 数学函数(abs, min, max)
├── + 文件 I/O(沙盒内虚拟文件系统)
└── + 标准库子集

2.2 渐进式解锁机制

// 用户学习进度追踪
public class LearningProgress {
    public int Level { get; set; } = 1;
    
    public HashSet<string> CompletedExercises { get; set; } = new();
    public HashSet<string> UnlockedFeatures { get; set; } = new();
    
    // 检查特性是否可用
    public bool CanUse(string feature) => feature switch {
        "break" => Level >= 2,
        "multi_array" => Level >= 3,
        "string_ops" => Level >= 4,
        "file_io" => Level >= 4,
        _ => true  // Phase 1 特性默认可用
    };
    
    // 完成练习后解锁新特性
    public void CompleteExercise(string exerciseId) {
        CompletedExercises.Add(exerciseId);
        
        // 完成链表练习 → 解锁 break/continue
        if (exerciseId == "linked_list_basics" && !UnlockedFeatures.Contains("break")) {
            UnlockedFeatures.Add("break");
            UnlockedFeatures.Add("continue");
            NotifyUser("🎉 解锁新语法:break / continue!");
        }
        
        // 完成树练习 → 解锁多维数组
        if (exerciseId == "binary_tree_traversal" && !UnlockedFeatures.Contains("multi_array")) {
            UnlockedFeatures.Add("multi_array");
            NotifyUser("🎉 解锁新语法:多维数组!");
        }
    }
}

// 编译器根据用户等级决定是否支持语法
public class ProgressiveCompiler {
    public CompileResult Compile(string source, LearningProgress progress) {
        var lexer = new Lexer(source, progress);  // 传入进度
        var tokens = lexer.Tokenize();
        
        // 如果用户还没解锁 break,遇到 break 时报特殊错误
        if (!progress.CanUse("break") && tokens.Any(t => t.Type == TokenType.BREAK)) {
            return CompileResult.Error(
                "break 语句将在「数据结构篇」解锁。\n" +
                "当前请使用 return 或调整循环条件。\n\n" +
                "💡 完成「链表基础」练习后即可解锁!"
            );
        }
        
        // ... 正常编译
    }
}

2.3 向后兼容性保证

Phase 1 代码
  ↓
Phase 2 编译器(完全兼容)
  ↓
Phase 3 编译器(完全兼容)
  ↓
Phase 4 编译器(完全兼容)

所有旧代码在新版本中都能正常运行。
只增加语法,不修改已有语法语义。

3. 数据结构教学支持

3.1 当前子集支持的数据结构

载体口径(2026-09-27 注):下表与本节 §3.2 的 vis_array() / vis_list() 等 vis_* 内置函数属设计稿形态,后端从未实现(native/src 全量零命中)——现役等价物是引擎自动产出的 vis_events 协议载荷(零侵入检测,AlgorithmMatch / StepPayload 帧内承载),渲染由下游完成。本节保留为设计意图与缺口论证的原始口径。

数据结构 实现方式 可视化支持 诊断支持
数组 原生 int[] vis_array() 越界检测
动态数组 malloc + 指针 vis_array() + 内存视图 泄漏检测
单链表 struct Node { int val; Node* next; } vis_list() 断链检测、泄漏检测
双链表 struct DNode { int val; DNode* prev; DNode* next; } vis_list() 断链检测
栈(数组) int[] + top 索引 vis_stack() 上溢/下溢检测
栈(链表) 链表 vis_stack() 断链检测
队列(数组) 循环数组 vis_queue() 上溢/下溢检测
队列(链表) 链表 + head/tail vis_queue() 断链检测
二叉树 struct TreeNode { int val; TreeNode* left; TreeNode* right; } vis_tree() 递归深度检测
图(邻接矩阵) 二维数组(Phase 3) vis_graph() -
图(邻接表) 数组 + 链表混合 vis_graph() -

3.2 数据结构专用内置函数

// ===== 数组可视化 =====
void vis_array(const char* name, int arr[], int n);
void vis_array_highlight(const char* name, int index, const char* color);
    // color: "compare"(橙), "swap"(粉), "sorted"(绿), "active"(蓝)
void vis_array_range(const char* name, int begin, int end, const char* color);
    // 高亮一个区间,如 vis_array_range("arr", 2, 5, "range");

// ===== 链表可视化 =====
void vis_list_node(int id, int value, int nextId);
void vis_list_highlight(int id, const char* color);
void vis_list_edge(int fromId, int toId);
void vis_list_pointer(const char* name, int nodeId);
    // 显示头指针/尾指针指向

// ===== 树可视化 =====
void vis_tree_node(int id, int value, int leftId, int rightId);
void vis_tree_highlight(int id, const char* color);
void vis_tree_edge(int fromId, int toId);

// ===== 栈/队列可视化 =====
void vis_stack_push(const char* name, int value);
void vis_stack_pop(const char* name);
void vis_stack_highlight(const char* name, int index, const char* color);

void vis_queue_enqueue(const char* name, int value);
void vis_queue_dequeue(const char* name);

// ===== 图可视化(Phase 3)=====
void vis_graph_node(int id, int value);
void vis_graph_edge(int fromId, int toId, int weight);
void vis_graph_highlight_node(int id, const char* color);
void vis_graph_highlight_edge(int fromId, int toId, const char* color);

// ===== 通用调试 =====
void vis_step(int line);                    // 高亮当前执行行
void vis_variable(const char* name, int value);
void vis_pointer(const char* name, void* ptr);
void vis_message(const char* text);         // 显示文字消息

3.3 数据结构专用的诊断和修复

3.3.1 链表断链检测

// 学生代码:危险的链表操作
void deleteNode(struct ListNode* head, int val) {
    struct ListNode* p = head;
    while (p != NULL) {
        if (p->val == val) {
            p = p->next;        // ❌ 直接移动,丢失被删除节点的前驱
            free(p);             // ❌ 先移动再 free,free 的是下一个节点!
        }
        p = p->next;
    }
}

运行时诊断:

⚠️ 链表操作警告(第 6~7 行)

你的操作顺序可能导致链表断裂或错误释放内存。

执行轨迹:
  step 5: p 指向 node1(val=3)
  step 6: p = p->next  → p 现在指向 node2
  step 7: free(p)      → 释放了 node2!但 node1->next 还指向 node2!

🔴 问题:
  1. 你先移动了 p,再 free(p),结果 free 的是下一个节点
  2. node1->next 变成了悬垂指针

✅ 正确顺序:
  struct ListNode* temp = p->next;  // 先保存下一个节点
  p->next = temp->next;              // 跳过要删除的节点
  free(temp);                        // 再释放

📚 [链表删除操作详解]

3.3.2 内存泄漏检测(链表)

// 学生代码:忘记释放链表
struct ListNode* createList(int n) {
    struct ListNode* head = NULL;
    for (int i = 0; i < n; i++) {
        struct ListNode* node = malloc(sizeof(struct ListNode));
        node->val = i;
        node->next = head;
        head = node;
    }
    return head;  // ❌ 函数结束时没有释放链表
}

程序结束时的内存诊断:

⚠️ 内存泄漏检测

程序结束时,以下内存未被释放:

地址        大小    分配位置              类型
0x1000      8 字节  createList 第 5 行    struct ListNode
0x1008      8 字节  createList 第 5 行    struct ListNode
0x1010      8 字节  createList 第 5 行    struct ListNode
...(共 5 个节点)

💡 建议:在程序结束前遍历链表,逐个 free:
  while (head != NULL) {
      struct ListNode* temp = head;
      head = head->next;
      free(temp);
  }

📚 [什么是内存泄漏?] [如何正确释放链表?]

3.3.3 递归深度检测(树遍历)

// 学生代码:缺少终止条件检查
void preorder(struct TreeNode* root) {
    // ❌ 忘记检查 root == NULL
    vis_tree_highlight(root->id, "active");
    printf("%d ", root->val);
    preorder(root->left);
    preorder(root->right);
}

运行时检测:

😵 栈溢出(递归深度超过 1000)

递归函数 preorder 在第 3 行被无限调用。

执行轨迹:
  call 1: root = node1
  call 2: root = node1->left = NULL
  call 3: root = NULL->left = ???   ← 崩溃!

🔴 问题:你没有检查 root 是否为 NULL 就访问了 root->left。

✅ 修复:
  void preorder(struct TreeNode* root) {
      if (root == NULL) return;   ← 添加终止条件
      // ...
  }

💡 记忆口诀:递归函数第一行,先写终止条件!

3.4 代码模板库(88 个内置模板:82 C + 6 C++,C++ 线随砍 C++ 裁定冻结于 oracle)

排序算法(9)

  • 冒泡排序、选择排序、插入排序、快速排序、归并排序、堆排序、希尔排序、计数排序、基数排序

查找算法(2)

  • 线性查找、二分查找

图算法(7)

  • BFS 广度优先搜索、DFS 深度优先搜索
  • Prim 最小生成树、Kruskal 最小生成树
  • Dijkstra 最短路径、Floyd 最短路径
  • 拓扑排序

动态规划(2)

  • 斐波那契数列、01 背包

数据结构(22)

  • 顺序表、链表节点/头插/尾插/遍历/删除/双向链表/循环链表/静态链表
  • 二叉树节点/先序/中序/后序/层序遍历、BST 插入与查找、线索二叉树、哈夫曼树、AVL 树
  • 链栈、链队列、循环队列、哈希表(线性探测)、并查集

字符串(3)

  • 字符串反转、朴素模式匹配(BF)、KMP 模式匹配

基础/递归(7)

  • 数组遍历、指针交换两数、GCD 欧几里得、素数判断、约瑟夫环
  • 阶乘、递归斐波那契、汉诺塔

每个模板均支持:

  • 参数化占位符:/*__PARAM_n__*/ 5 形态(现行唯一语法,见模板维护指南.md §3;切割前旧 Dart 语法 {{n:5}} 已移除,参数收集 UI 属前端载体)
  • 交互式教程:逐步骤高亮代码行,关键行可展开中文解释(教程渲染属前端载体)
  • 自动编译运行:教程最后一步自动插入生成代码、编译并启动统一模式
  • 算法步骤语义标注:运行时根据源码行特征和变量值推断当前阶段,生成中文教学描述

载体说明(2026-09-11):切割前上述"参数收集弹窗 / 教程面板 / 自动编译运行"由前端组件实现,已随前端切割迁出(历史资产,见标签 before-frontend-split)。本仓库现只保留模板源 templates/<key>/(source.c + meta.yaml)与后端语义标注能力;模板源的归属见 CHANGELOG.md [Unreleased] Removed 段(暂保留待社区前端或 wasm 出口认领)。


4. 知识图谱:从语法到算法到数据结构

知识图谱
│
├─ 基础语法
│   ├─ 变量与类型 → int, 内存中的表示
│   ├─ 表达式 → 运算符优先级
│   ├─ 控制流 → if/else, for, while
│   └─ 函数 → 参数传递, 递归
│
├─ 内存与指针(解锁条件:掌握基础语法)
│   ├─ 数组 → 连续内存, 索引计算
│   ├─ 指针 → &取地址, *解引用, NULL
│   ├─ 栈与堆 → 局部变量, malloc/free
│   └─ 常见错误 → 越界, 空指针, 悬垂指针, 泄漏
│
├─ 数据结构基础(解锁条件:掌握指针)
│   ├─ 链表 → 单链表, 双链表, 插入, 删除, 遍历
│   ├─ 栈 → 数组实现, 链表实现, 应用
│   ├─ 队列 → 循环数组, 链表实现, 应用
│   └─ 树 → 二叉树, 遍历(前/中/后/层序), BST
│
├─ 算法基础(解锁条件:掌握数组和循环)
│   ├─ 排序 → 冒泡, 选择, 插入, 快排, 归并
│   ├─ 搜索 → 线性, 二分
│   └─ 递归 → 阶乘, 斐波那契, 树遍历
│
└─ 进阶数据结构(解锁条件:掌握基础数据结构)
    ├─ 图 → 邻接矩阵, 邻接表, BFS, DFS
    ├─ 高级树 → AVL, 红黑树(概念)
    └─ 哈希表 → 概念, 冲突处理

每个知识点关联:

  • 错误码(数组越界 → 数组知识卡片)
  • 练习题(完成练习解锁下一个知识点)
  • 可视化演示(数组排序动画、链表操作动画)

5. 实施优先级

Phase 1:核心子集 + 基础修复(✅ 已完成)

  • C 子集编译器(int, float, double, long long, 数组, 指针, struct, union, enum, typedef, if/for/while/do-while/switch, 函数, malloc/free/realloc)
  • 基础修复(语法错误、数组越界、空指针、未初始化)
  • 内存视图 + 指针视图 + 堆内存可视化
  • 结构化诊断系统(错误码 + 知识卡片 + 自动修复建议)
  • 基础算法模板(冒泡排序、二分查找)

Phase 2:算法修复 + 数据结构基础(✅ 已完成)

  • 算法模式识别系统(17+ 种算法/数据结构检测)
  • 算法步骤语义标注(27 种算法/数据结构操作预定义步骤模板)
  • 运行时验证(Property-based Testing)— ⚠️ 载体为前端,已随前端切割迁出(后端无对应模块,见 §1.4.1 诚实记录)
  • 执行轨迹分析(TraceAnalyzer -> RootCauseHint)
  • 代码模板参数化 + 交互式教程(57 个模板)— 模板源现保留在 templates/<key>/,教程渲染属前端载体(已迁出)
  • 链表、栈、队列、二叉树可视化(CustomPainter)— ⚠️ 渲染层为前端载体,已迁出;后端保留可视化事件载荷(vis_events)
  • 数据结构专用诊断(断链检测、泄漏检测、Use-After-Free/Double-Free 运行时检测)

Phase 3:进阶扩展(✅ 已完成 / 🔄 进行中)

  • 更多算法模板(快排、归并、堆排序、希尔排序、计数排序)
  • 图算法模板(BFS、DFS)
  • 多维数组
  • 知识图谱系统(24 概念节点 + 30+ 关系边)
  • 用户学习进度追踪(LearningProgress + SharedPreferences 持久化)— ⚠️ 进度状态与持久化均为前端载体,已随前端切割迁出;后端保留推荐引擎 native/src/diagnostics/learning_path.rs(LearningPath / PathStep / recommend_learning_paths),进度状态需由消费方自行持有
  • 认知推理 P0~P3(根因分析 -> 教学推理 -> 知识图谱 -> 代码理解/意图推断)
  • 语义智能补全 v2(5 种上下文感知补全)
  • 模板 JIT 加速(Trace-based Loop Accelerator)
  • Dijkstra 算法模板(templates/dijkstra/,已进标注 golden)
  • 社区贡献的算法模板

Phase 4:完整生态(后续)

  • 字符串操作(strlen, strcpy, strcmp, strcat)
  • 文件 I/O(fopen, fclose, fgets, fputs, fread, fwrite)
  • 更多标准库函数(qsort, fprintf, atoi, putchar, srand/rand, memset)
  • OCR 照片导入 — 已随前端切割作废(相机/图库权限与图像输入属前端能力,本仓库为纯后端且已放弃原生移动端)
  • 在线判题(OJ)集成 — 保留(headless 判分可由三出口承载:capi / vitro_cli serve / wasm32)

6. 总结

算法层面的错误能修复吗?

不能直接"自动修复",但可以"智能诊断 + 引导修复"。

层级 能力 方式
语法/语义 自动修复 编译器直接发现和修复
算法模式 模板对比 + 建议 "你的冒泡排序边界应该是 n-1"
运行时验证 测试驱动诊断 "排序结果不正确,元素 8 丢失了"
执行轨迹 轨迹分析 "第 3 趟没有比较 arr[2] 和 arr[3]"
逻辑漏洞 仅提示 "递归函数缺少终止条件检查"

核心原则:算法修复不是代写代码,而是帮助学生理解算法逻辑。

如何支持数据结构?

当前子集(int + 指针 + struct + malloc)已经支持链表、树、栈、队列的全部基础操作。

需要补充:

  1. 内置可视化函数(vis_list, vis_tree, vis_stack)
  2. 数据结构专用诊断(断链检测、泄漏检测、递归深度)
  3. Starter Code 模板(ListNode, TreeNode, 辅助函数)
  4. 逐步解锁机制(完成链表练习后解锁 break/continue 和树的相关内容)

子集扩展的关键

不是一次性做大,而是按需渐进式扩展:

  • Phase 1 足够支持排序、搜索、递归
  • Phase 2 解锁 break/continue 后支持更复杂的链表操作
  • Phase 3 解锁多维数组后支持图的邻接矩阵
  • 每个新特性都有明确的学习路径和解锁条件

7. 本次翻新发现的实现缺口(2026-09-11)

本节为 2026-09-11 文档翻新(去前端化 + 失效引用修复)过程中核实到的文档与实现不一致项,按"诚实记录"原则集中列出。结论均有本仓库实际代码 / git 证据支撑;本次翻新只做文档记录,不新增或修改任何 .rs 代码。

# 缺口 原文(翻新前)表述 核实结果 证据
1 运行时算法属性验证(Level 2) §1.4 记"✅ 已实现",实现位置 native/src/engine/algorithm_validator.rs(Rust 后端)+ 前端 Rust 后端无实现:validate_algorithm() / ValidationResult 在 native/ 下无任何定义(整目录检索零命中);native/src/engine/ 现仅有 compile_pipeline.rs / session_ops.rs / completion/。实际载体是前端侧算法验证模型(AlgorithmTestCase / AlgorithmValidationResult + 手写属性用例),已随前端切割迁出 git log --all -S "validate_algorithm" 仅 1 个提交 a29a056;历史中唯一相关路径为 CideFlutter/lib/models/algorithm_validation.dart(历史资产,已迁出)——标签 before-frontend-split 中存在,HEAD 已无
2 学习进度追踪 Phase 3 记"用户学习进度追踪(LearningProgress + SharedPreferences 持久化)"已实现 后端无 LearningProgress(native/src 零命中);后端只提供推荐引擎 native/src/diagnostics/learning_path.rs(LearningPath / PathStep / recommend_learning_paths)。进度状态与持久化原为前端载体,已迁出(历史资产,已迁出) native/src 检索 LearningProgress 零命中;learning_path.rs 实际导出如上
3 AlgorithmMatch 的归属(澄清,非缺口) §1.4.1 / §1.4.3 将该结构体与"验证"绑定 AlgorithmMatch 在 Rust 确实存在(native/src/session.rs),但它是算法检测结果(name / display_name / func_name / confidence / suggestion / line / vis_events),不含任何验证逻辑;检测实现为 native/src/compiler/algorithm_detector/,经 compile_pipeline 写入会话 见 §1.4.1 / §1.4.2 已更正的正文
4 模板参数替换与教程渲染 API 本文 §3.4 与 ARCHIVE_数据结构模板路线图.md §7 记 buildCode() / focusLines / LineExplanation 为验收项 均为前端载体 API,已迁出;本仓库现只保留模板源 templates/<key>/(source.c + meta.yaml),加载 / 参数替换 / 教程渲染代码、scripts/test_templates.py 均已移除;scripts/sync_templates.py 已于 2026-09-12(R4 G1)恢复后端职能 CHANGELOG.md [Unreleased] Removed 段

当前可用的替代验证路径(后端,已核实):

  • 算法检测:native/src/compiler/algorithm_detector/(detect_algorithms,由 native/src/engine/compile_pipeline.rs 调用);
  • 模板运行正确性:native/tests/cases_template_generated/ 的模板生成用例(与 Clang golden 对照,属 Shadow 防线);
  • 算法步骤语义标注:native/crates/vitro_algorithm_steps/(27 种算法预定义步骤模板)。

结论:缺口 1、2 属"能力原本只在前端载体、后端从未落地",前端切割后在本仓库为无实现状态;需由维护者决策是后端重建(按三出口 JSON 载荷形状)还是随模板源一并交社区前端认领。

补记(2026-09-13):

  • 缺口 4 中 scripts/sync_templates.py 已由重构批次 R4 G1 于 2026-09-12 从历史提交恢复(纯后端形态:渲染模板源 + Clang Golden,见 模板维护指南.md §4.2);test_templates.py 仍未恢复。
  • 缺口 1(运行时算法属性验证,即项目路线图 G9)的重建排期锚定在 C# 主线 CS5(见 CSharp前端引入计划.md;缺口 2(学习进度追踪)维持"进度状态由消费方持有"口径。