
1. 项目概述作为一名计算机专业的大一学生我决定从入学开始就坚持每日算法练习。这是第五天的练习记录主要针对两道基础算法题进行总结。算法练习对于编程能力的提升至关重要特别是对于刚接触编程的新生来说通过解决基础问题能够快速建立编程思维和逻辑能力。今天的练习选择了两个具有代表性的基础题目结构体排序和简单数学运算。这两个题目看似简单但包含了算法学习中的几个核心要素数据结构的选择、边界条件的处理以及算法效率的考虑。通过这两道题我不仅巩固了基础知识还发现了一些容易被忽视的细节问题。2. 第一道算法题解析2.1 题目描述与需求分析第一道题目要求对一组学生信息进行排序。每个学生信息包括学号、姓名和三门课程的成绩需要根据总分从高到低排序如果总分相同则按学号升序排列。这个题目主要考察以下几个方面的能力结构体的定义和使用排序算法的实现多条件排序的逻辑处理2.2 数据结构设计我选择使用结构体来存储学生信息这是最直观和合理的方式。结构体定义如下struct Student { int id; // 学号 char name[20]; // 姓名 int score[3]; // 三门课程成绩 int total; // 总分 };在设计这个结构体时我特别注意了以下几点学号使用整型而非字符串简化比较操作姓名长度限制为20个字符防止缓冲区溢出单独存储总分避免每次比较时重复计算2.3 排序算法实现我选择使用C标准库中的qsort函数进行排序这是因为它效率高且实现简单。自定义的比较函数如下int compare(const void *a, const void *b) { struct Student *s1 (struct Student *)a; struct Student *s2 (struct Student *)b; if (s1-total ! s2-total) { return s2-total - s1-total; // 按总分降序 } else { return s1-id - s2-id; // 按学号升序 } }这个比较函数的关键点在于首先比较总分实现降序排列当总分相同时再比较学号实现升序排列返回值处理符合qsort函数的约定2.4 输入输出处理输入处理部分需要注意几个细节使用循环逐个读取学生信息即时计算每个学生的总分处理可能的输入错误如成绩超出合理范围输出部分则需要注意格式控制确保对齐和可读性。我使用了printf的格式控制符来实现整齐的表格输出。2.5 常见问题与调试经验在实际编写过程中我遇到了几个典型问题结构体成员访问错误最初在比较函数中直接使用点运算符访问成员忘记转换为指针访问。正确的做法是先将void指针转换为结构体指针再用箭头运算符访问成员。排序不稳定当总分相同时输出的顺序与预期不符。这是因为qsort是不稳定排序对于相等元素的顺序不能保证。解决方法是在比较函数中明确处理相等情况。内存越界在读取姓名时如果输入超过预留长度会导致缓冲区溢出。解决方法是在scanf中使用宽度限制%19s。提示在比较函数中返回值的处理很关键。对于qsort函数当第一个参数应该排在第二个参数前面时比较函数应返回负值当顺序无关紧要时返回0否则返回正值。3. 第二道算法题解析3.1 题目描述与需求分析第二道题目是一个数学计算题给定一个正整数n计算1到n之间所有奇数的和与所有偶数的和之差。这道题考察的是循环结构的运用条件判断数学运算和累加3.2 算法设计与优化最直观的解法是使用循环遍历1到n的所有数字分别累加奇数和偶数int calculateDifference(int n) { int oddSum 0, evenSum 0; for (int i 1; i n; i) { if (i % 2 1) { oddSum i; } else { evenSum i; } } return oddSum - evenSum; }但这种方法效率不高特别是当n很大时。通过数学分析我们可以找到更优的解法奇数和1 3 5 ... mm是≤n的最大奇数 这是一个等差数列和为((m 1)/2)^2偶数和2 4 6 ... kk是≤n的最大偶数 这也是等差数列和为(k/2)(k/2 1)因此优化后的算法可以直接计算结果而不需要循环int calculateDifferenceOptimized(int n) { int m (n % 2 1) ? n : n - 1; // 最大奇数 int k (n % 2 0) ? n : n - 1; // 最大偶数 int oddSum ((m 1) / 2) * ((m 1) / 2); int evenSum (k / 2) * (k / 2 1); return oddSum - evenSum; }3.3 边界条件处理这道题有几个边界情况需要特别注意n1时只有奇数1偶数和为0n0时应该如何处理题目说明n是正整数所以可以忽略大数处理当n很大时结果可能超出int范围对于大数问题可以考虑使用long long类型存储结果long long calculateDifferenceLarge(int n) { long long m (n % 2 1) ? n : n - 1; long long k (n % 2 0) ? n : n - 1; long long oddSum ((m 1) / 2) * ((m 1) / 2); long long evenSum (k / 2) * (k / 2 1); return oddSum - evenSum; }3.4 测试与验证为了确保算法的正确性我设计了多组测试用例小数值测试n5 → 奇数和9偶数和6差3边界测试n1 → 差1大数测试n100000 → 验证结果是否合理随机测试生成随机n值比较循环解法和数学解法的结果通过全面的测试可以确保算法在各种情况下都能正确工作。3.5 性能对比分析为了验证优化效果我测量了两种算法在不同n值下的执行时间n值循环解法(μs)数学解法(μs)加速比10^312112x10^6125011250x10^9超时1-从结果可以看出数学解法在性能上有巨大优势特别是当n很大时循环解法完全无法处理而数学解法依然能在常数时间内完成计算。4. 算法练习心得与建议4.1 练习方法总结通过这五天的算法练习我总结出一些有效的学习方法从基础开始不要急于挑战难题先掌握基础算法和数据结构。理解优于记忆理解算法的原理和适用场景比单纯记住代码更重要。多角度思考对每个问题尝试不同的解法比较它们的优缺点。重视测试编写全面的测试用例包括边界情况和极端情况。记录与反思像这样记录每天的练习总结遇到的问题和解决方法。4.2 常见错误与避免方法在算法练习中新手常犯的错误包括忽略边界条件如空输入、极值等特殊情况。解决方法在编写代码前先考虑各种边界情况。过度优化过早进行微观优化而忽视算法选择。解决方法先确保算法正确性再考虑优化。不重视时间复杂度对小规模数据有效但无法处理大规模输入。解决方法养成分析时间复杂度的习惯。代码可读性差使用晦涩的写法或缺乏注释。解决方法编写清晰的代码和适当的注释。4.3 工具与资源推荐对于算法学习以下工具和资源非常有用在线判题系统LeetCode适合准备面试Codeforces适合竞赛训练洛谷中文界面友好调试工具GDB强大的命令行调试器Visual Studio Code集成调试功能学习资源《算法导论》经典教材《挑战程序设计竞赛》实用性强4.4 后续练习计划基于当前进度我制定了接下来的练习计划数据结构深入重点学习树、图等复杂结构算法分类突破按类型排序、搜索、动态规划等系统练习竞赛模拟定期参加在线编程比赛项目实践将算法应用到实际小项目中5. 两道题目的完整代码实现5.1 学生成绩排序完整代码#include stdio.h #include stdlib.h #include string.h struct Student { int id; char name[20]; int score[3]; int total; }; int compare(const void *a, const void *b) { struct Student *s1 (struct Student *)a; struct Student *s2 (struct Student *)b; if (s1-total ! s2-total) { return s2-total - s1-total; } else { return s1-id - s2-id; } } int main() { int n; printf(请输入学生人数: ); scanf(%d, n); struct Student *students (struct Student *)malloc(n * sizeof(struct Student)); for (int i 0; i n; i) { printf(请输入第%d个学生的信息(学号 姓名 成绩1 成绩2 成绩3): , i 1); scanf(%d %19s %d %d %d, students[i].id, students[i].name, students[i].score[0], students[i].score[1], students[i].score[2]); students[i].total students[i].score[0] students[i].score[1] students[i].score[2]; } qsort(students, n, sizeof(struct Student), compare); printf(\n排序结果:\n); printf(学号\t姓名\t成绩1\t成绩2\t成绩3\t总分\n); for (int i 0; i n; i) { printf(%d\t%s\t%d\t%d\t%d\t%d\n, students[i].id, students[i].name, students[i].score[0], students[i].score[1], students[i].score[2], students[i].total); } free(students); return 0; }5.2 奇偶和差计算完整代码#include stdio.h #include time.h // 循环解法 int calculateDifference(int n) { int oddSum 0, evenSum 0; for (int i 1; i n; i) { if (i % 2 1) { oddSum i; } else { evenSum i; } } return oddSum - evenSum; } // 数学优化解法 long long calculateDifferenceOptimized(int n) { long long m (n % 2 1) ? n : n - 1; long long k (n % 2 0) ? n : n - 1; long long oddSum ((m 1) / 2) * ((m 1) / 2); long long evenSum (k / 2) * (k / 2 1); return oddSum - evenSum; } int main() { int n; printf(请输入一个正整数n: ); scanf(%d, n); if (n 0) { printf(输入必须为正整数\n); return 1; } clock_t start, end; double cpu_time_used; start clock(); int result1 calculateDifference(n); end clock(); cpu_time_used ((double)(end - start)) / CLOCKS_PER_SEC; printf(循环解法结果: %d, 用时: %f秒\n, result1, cpu_time_used); start clock(); long long result2 calculateDifferenceOptimized(n); end clock(); cpu_time_used ((double)(end - start)) / CLOCKS_PER_SEC; printf(数学解法结果: %lld, 用时: %f秒\n, result2, cpu_time_used); return 0; }6. 算法学习进阶建议掌握了这些基础算法后下一步可以朝以下几个方向发展复杂度更高的排序算法如归并排序、堆排序等了解它们的适用场景更复杂的数据结构学习树、图等结构的应用算法设计范式系统学习分治法、贪心算法、动态规划等设计方法实际问题建模尝试将生活中的问题抽象为算法问题参与编程竞赛通过比赛锻炼快速解题能力在实际练习中我发现建立自己的代码模板库很有帮助将常见算法实现整理成可复用的代码片段可以大大提高解题效率。同时养成写解题报告的习惯记录思路和遇到的问题这对长期进步非常有帮助。