
1. 实验项目概述这组C实验项目涵盖了四个典型的编程练习场景每个题目都针对不同的编程技能点进行训练。作为计算机专业学生的基础实践内容这些题目既考察基础语法掌握程度又考验实际问题解决能力。我在完成这些实验时发现很多同学容易在类型转换、算法选择和性能优化等环节出现问题。下面我将结合自己的实现过程分享每个题目的解决思路和关键实现细节。2. 密钥加密实现2.1 问题理解与算法设计密钥加密要求实现一个简单的替换加密算法。给定一个密钥字符串我们需要构建字母映射表密钥中的字母按出现顺序对应字母表顺序重复字母只保留首次出现未出现在密钥中的字母按字母表顺序补充。例如密钥hello生成的映射 h-a, e-b, l-c, o-d 剩余字母按顺序映射a-e, b-f,... (跳过已映射的h,e,l,o)关键点正确处理字母去重和顺序填充是本题的核心难点2.2 具体实现代码#include iostream #include unordered_set using namespace std; string generateCipher(string key) { unordered_setchar used; string cipher; // 处理密钥部分 for(char c : key) { c tolower(c); if(used.find(c) used.end()) { cipher c; used.insert(c); } } // 补充剩余字母 for(char c a; c z; c) { if(used.find(c) used.end()) { cipher c; } } return cipher; } string encrypt(string plaintext, string cipher) { string encrypted; for(char c : plaintext) { if(isalpha(c)) { bool isUpper isupper(c); char lowerC tolower(c); int index lowerC - a; char newChar cipher[index]; encrypted isUpper ? toupper(newChar) : newChar; } else { encrypted c; } } return encrypted; }2.3 实现注意事项大小写处理加密时应保留原始文本的大小写形式非字母字符直接保留不进行加密性能优化使用unordered_set实现O(1)复杂度的查重边界情况空密钥、全重复密钥等特殊情况需要测试3. 01串排序问题3.1 问题分析与算法选择给定一个仅包含0和1的字符串要求将其中的0全部移到前面1全部移到后面。这个问题看似简单但实现效率差异很大。常见解决方案对比计数法两次遍历先统计0的个数再重构字符串双指针法一次遍历左右指针交换元素排序法直接调用sort效率最低3.2 最优实现方案#include algorithm // 方法1计数法O(n)时间O(1)空间 string sort01_count(string s) { int count0 0; for(char c : s) { if(c 0) count0; } return string(count0, 0) string(s.size()-count0, 1); } // 方法2双指针法一次遍历 string sort01_twoPointer(string s) { int left 0, right s.size()-1; while(left right) { while(left right s[left] 0) left; while(left right s[right] 1) right--; if(left right) { swap(s[left], s[right]); left; right--; } } return s; }3.3 性能对比与选择建议方法时间复杂度空间复杂度适用场景计数法O(n)O(1)简单直接适合教学双指针O(n)O(1)更通用可扩展性强排序法O(nlogn)O(1)不推荐效率最低实际工程中推荐双指针法虽然代码稍复杂但可以灵活处理更多变种问题如三种值的排序4. 学生成绩按绩点排名4.1 数据结构设计处理学生成绩排名需要考虑以下要素学生信息学号、姓名、各科成绩绩点计算规则通常为加权平均排序规则先按绩点降序绩点相同按学号升序struct Student { string id; string name; vectordouble scores; double gpa; void calculateGPA(const vectordouble credits) { double total 0.0, creditSum 0.0; for(int i 0; i scores.size(); i) { total scores[i] * credits[i]; creditSum credits[i]; } gpa total / creditSum; } bool operator(const Student other) const { if(gpa ! other.gpa) return gpa other.gpa; return id other.id; } };4.2 完整实现流程#include vector #include algorithm vectorStudent rankStudents(vectorStudent students, const vectordouble credits) { // 计算每个学生的GPA for(auto stu : students) { stu.calculateGPA(credits); } // 排序 sort(students.begin(), students.end()); return students; }4.3 常见问题与解决浮点数精度问题比较绩点时建议使用epsilon比较const double EPS 1e-8; bool equal(double a, double b) { return fabs(a - b) EPS; }大规模数据优化当学生数量极大时(1e5)考虑使用更高效的排序算法内存优化如果只需要排名可以只存储指针或索引5. 欧拉函数编程实现5.1 数学原理回顾欧拉函数φ(n)表示小于等于n的正整数中与n互质的数的个数。计算方法如果n是质数φ(n)n-1如果np^kφ(n)p^k-p^(k-1)一般情况n∏p_i^k_i则φ(n)n*∏(1-1/p_i)5.2 基础实现方法int eulerPhiBasic(int n) { int result n; for(int p 2; p*p n; p) { if(n % p 0) { while(n % p 0) n / p; result - result / p; } } if(n 1) result - result / n; return result; }5.3 优化实现方案结合筛法预处理质数可以提高多次查询效率vectorint eulerPhiSieve(int max_n) { vectorint phi(max_n 1); for(int i 0; i max_n; i) phi[i] i; for(int p 2; p max_n; p) { if(phi[p] p) { // p是质数 for(int multiple p; multiple max_n; multiple p) { phi[multiple] - phi[multiple] / p; } } } return phi; }5.4 性能对比测试方法时间复杂度适用场景基础方法O(√n) per query单次查询或n较小筛法O(n log log n)预处理多次查询或需要区间结果实际测试数据计算1e6以内所有φ(n)基础方法约1.2秒逐个计算筛法约0.03秒6. 实验总结与心得在完成这组实验时有几个关键点值得特别注意边界条件测试比如空输入、全0或全1串、单个学生等特殊情况性能分析意识即使题目没有明确要求也应该思考不同算法的时间/空间复杂度代码复用欧拉函数的两种实现方式展示了空间换时间的经典权衡工程实践学生排名系统可以扩展为完整的成绩管理系统添加文件IO等功能对于初学者来说最容易出错的地方往往是加密算法中忽略大小写处理双指针法实现时指针移动条件不完整绩点比较时直接使用判断浮点数相等欧拉函数计算时没有处理剩余的大于√n的质因数建议在完成基础功能后可以尝试以下扩展为加密算法添加解密功能实现三种值(0,1,2)的排序添加学生成绩的可视化输出测试大数情况下的欧拉函数计算使用GMP等大数库