C#刷LeetCode的高效闭环:每日复习、语法优化与调试模板

发布时间:2026/10/22 5:42:44

C#刷LeetCode的高效闭环:每日复习、语法优化与调试模板 连续坚持了几个月每日打卡某天回头复盘时突然意识到一个问题LeetCode每日刷题这件事我一天没断可真要我临时做一道上周做过的同类题竟然只能想起一个模糊的框架细节全丢。如果用的是C#这种感觉会更明显——讨论区里C、Java的题解铺天盖地C#的版本得翻好几页才找到一个能用的拿过来跑通了却说不清楚它到底优在哪、坑在哪。后来我停掉了“今天又刷了一题”这种自我安慰把每日刷题改造成了一套完整的训练流程每天只花40分钟左右重点是“新题复习总结”三段闭环坚持一个多月后旧题的复现率明显上来了。这篇文章就把我完整的玩法、C#刷题时的语法取舍、以及我用着很顺的调试模板都摊开聊一遍。1. 每天打卡式刷题一百天后脑子还是空的先想清楚“每日刷题”到底在刷什么1.1 把“刷了多少题”当KPI是最容易掉进去的陷阱如果你也习惯在日历App里把每天的任务画成绿格子那下面这个场景你大概率不陌生状态好的时候点开一道Medium四十分钟做完状态差的时候看两分钟没有思路直接打开题解抄一遍然后告诉自己“今天也刷过了”。每周日看一眼统计面板绿色连续天数拉满内心毫无波澜甚至有点空虚。这种模式的核心问题在于我们把“每日刷题”这个动作本身当成了KPI而完全没有关注这个动作带来的能力增量。做算法题其实跟练乐器很像你每天按一小时琴键如果只是把谱子从头到尾弹一遍不针对难点单独拆解练一年也很难有明显的进步。刷题也是一样真正的成长发生在那段“思考为什么”的时间里而不是发生在“通过了所有测试用例”的瞬间。我在意识到这一点之后给自己定了一条硬规矩每天的新题时间只占整个流程的不到一半剩下一半以上都用来做复习和结构化的总结。哪怕这一天状态差到完全写不出题我也会把前一天做过的题重新默写一遍关键代码而不是打开评论区复制一段交差。这样做的直接收益是第二天再遇到类似题型时我不是“好像见过”的模糊感而是能清晰地写出第一步、第二步该做什么。1.2 题解看得多、复习得少等于反复回到起点C#选手刷题还有一个额外的劣势高质量的中文题解绝大多数是C或Java写的。所以很多C#玩家养成了一个习惯——看到题解就存GitHub仓库里收集了上百个链接收藏夹吃灰。然后某天偶尔翻出来发现里面的代码自己根本看不懂或者看懂了也忘了当时为什么收藏它。我自己也经历过这个阶段而且踩过一个很具体的坑遇到一道字符串处理题看了三篇题解每篇都用不同的解法我兴奋地都存了下来觉得自己“学到了三种解法”。结果一周后遇到同类型的变体题我一种都写不出来。原因很简单看题解时我是在“被动接收信息”而没有任何主动回忆的动作。接收信息的时候人会产生一种错觉觉得“我会了”但大脑里根本没有形成可调用的路径。后来我改了策略看题解只允许自己看两种。一种是明显更优的复杂度解比如从O(n^2)优化到O(n)另一种是能处理我没想到的边界情况的写法。看完之后必须做两件事第一把题解用自己的C#代码重写一遍并跑通第二在笔记里写一句话说明这个解法的核心思路和适用边界。如果做不到这两步这个题解对我而言等于不存在。这话听起来很绝对但对治“收藏即学会”的心理非常有效。1.3 刷题计划里必须出现的“复习缺口”模型聊完前面的误区接下来给一个我一直在用的规划模型叫“复习缺口”。一句话解释把已经掌握的题型当成存量资产每天的新题是增量如果你只盯增量你的真实水平永远由最近三五天的存量决定想有积累几乎不可能。以每天40分钟为例我的时间分配长这样前10分钟不查资料默写前一天题目的核心思路最好连代码骨架也要写出来中间15分钟做今日新题只看题目不先看题解后15分钟新题对照题解并且完成一句话总结如果新题没做出来这15分钟就用来复盘卡点在哪每周额外有一天不刷新题专门重新做本周错题或“需要复习”标记的题这个模型的核心不是“每日刷题”而是“每日复习”。因为遗忘曲线在前24小时下降最快第二天如果不碰一下昨天的内容到了第三天基本就清零了。反过来一旦你在第二天主动回忆了一次三天后、七天后还能轻松调出来的概率会大幅上升。我坚持了一个月左右就发现那些被标记为“复习”的旧题越来越不需要花时间这其实就是长期记忆形成的信号。2. 用C#写算法题字典、排序、字符串这三块要先理顺2.1 字典三板斧TryGetValue、GetValueOrDefault、GetValueRefOrAddDefault很多C#新手刷题时会写出这种代码if (dict.ContainsKey(key)) { dict[key]; } else { dict[key] 1; }这个写法的问题在于ContainsKey和索引器赋值各做了一次哈希查找相当于同一把钥匙开了两次锁。在数据量到十万级的时候性能差异不明显但LeetCode的测试用例普遍会把它拉到百万级这时候同一套逻辑用TryGetValue就要明显更稳。更推荐的标准写法是这样var dict new Dictionarystring, int(); foreach (string item in items) { if (dict.TryGetValue(item, out int count)) { dict[item] count 1; } else { dict[item] 1; } }如果刷题环境的.NET版本到了.NET 6以上还可以用更狠的写法直接拿引用改值ref int value ref CollectionsMarshal.GetValueRefOrAddDefault(dict, key, out bool exists); value;这种写法只做一次查找就能完成“不存在则初始化、存在则自增”在读多写多的场景下收益最明显。不过我必须提醒一句这是刷题向的优化写法它依赖内部实现细节普通业务代码里不要这样用可读性太差。你只需要知道有这个东西万一遇到卡时间的题目它能帮你提一点速。2.2 排序不是无脑Array.Sort稳定性的坑在这里C#里最常见的排序API就是Array.Sort和List.Sort但这两个方法默认都是不稳定的。什么概念呢如果两个元素在排序规则下“相等”稳定排序会保持它们原来的先后顺序不稳定排序不保证这一点。举个算法题里会踩坑的例子有一个数组存的是(val, index)结构你要按val升序排同时希望index小的排在前面。很多人会直接把数组丢给Array.Sort配一个只比较val的比较器然后发现结果里相同val的元素顺序变了。有些题目不要求稳定性怎么排都行但有些依赖原顺序的题这就直接WA。我现在的习惯是只要排序结果还需要依赖原始顺序就不赌Array.Sort的运气直接转成LINQ的OrderBy。OrderBy是稳定排序语义上也更明确var sorted pairs .OrderBy(p p.Value) .ThenBy(p p.Index) .ToList();虽然OrderBy带来的额外开销比Array.Sort大一点但在刷题场景下一次O(n log n)的排序开销完全可以接受。为了性能去用一个隐含不确定性语义的API然后把错误定位的锅甩给自己完全不划算。2.3 字符串拼接别用StringBuilder也不是万能算法题里的字符串处理场景特别多而C#的string是不可变类型每次操作都会生成一个新的字符串如果在一个循环里反复用拼接会产生大量中间对象直接拖慢执行速度严重时就是TLE。基础的替代方案是StringBuildervar sb new StringBuilder(); for (int i 0; i n; i) { sb.Append(...); }但我想多提一层有些字符串题目的本质其实是对字符数组的原地操作用StringBuilder反而绕了。比如字符串反转、去掉指定字符、逐字符处理再比较这类题直接声明char[]数组操作完再new string(arr)往往比StringBuilder更利落也更接近算法题想要的“操作数据”思维而不是“操作字符串API”的思维。char[] arr s.ToCharArray(); // 交换、赋值等操作 string result new string(arr);一句话总结拼接量小用无所谓拼接量大用StringBuilder涉及频繁修改单个字符时先考虑char[]。3. 三类高频题型的C#成套写法哈希计数、前缀和、双指针3.1 哈希计数从频次统计到K个高频词哈希计数是出现频率最高的套路一类典型的题是给一个字符串统计每个字符出现次数返回出现次数排名前K的字符。C#写起来很顺手public static IListchar TopKFrequentChars(string s, int k) { var dict new Dictionarychar, int(); foreach (char c in s) { if (dict.TryGetValue(c, out int cnt)) { dict[c] cnt 1; } else { dict[c] 1; } } return dict .OrderByDescending(p p.Value) .Take(k) .Select(p p.Key) .ToList(); }这段代码的时间复杂度是O(n m log m)n是字符串长度m是不同的字符数量。如果题目把数据量加大到极端可以再用桶排序优化成O(n)把频率相同的字符分到同一个桶里再用一个数组从频率高的桶往下扫。这个优化思路不用急着背下来先掌握基础的OrderBy版本遇到TLE再上桶优化否则容易一次性吃太多概念。这种“遍历计数 - 排序取前K”的套路能跑通一大半与频次相关的题包括数组里出现次数超过一半的元素、找出字符串中第一个不重复字符等核心都是先建字典再基于字典做二次处理。3.2 前缀和把“区间和查询”的时间复杂度降下来前缀和解决的核心问题是频繁计算子数组的和如果每次都重新累加O(n^2)逃不掉用前缀和数组一次计算就能把区间和查询降成O(1)的减法操作。有一类典型题是统计数组里有多少个子数组的和等于k。朴素的双重循环会超时标准解法就是“前缀和哈希表”组合public static int SubarraySum(int[] nums, int k) { var prefix new Dictionaryint, int { [0] 1 }; int sum 0; int ans 0; foreach (int x in nums) { sum x; if (prefix.TryGetValue(sum - k, out int cnt)) { ans cnt; } prefix[sum] prefix.GetValueOrDefault(sum, 0) 1; } return ans; }为什么字典初始化时要写[0] 1因为前缀和的起点是0当从头到某个位置的完整和正好等于k时需要从“空前缀”这一项里找到匹配。这个细节如果不加所有答案会少1属于这道题的高频踩坑点。这个套路的核心思想是我们不在遍历时重复计算区间而是把“之前出现过的前缀和”存进字典用目标值减去当前前缀和看能否在历史里找到对应的补数。理解了这个逻辑你会发现它能从连续子数组求和一路扩展到二维矩阵里的矩阵和问题本质上是同一套抽屉原理。3.3 双指针排序之后的两数之和边界如何处理双指针最经典的场景是从数组中找两个数使它们的和等于目标值。无序数组先排序然后用一左一右两个指针往中间逼public static int[] TwoSumSorted(int[] nums, int target) { Array.Sort(nums); int left 0; int right nums.Length - 1; while (left right) { int sum nums[left] nums[right]; if (sum target) { return new[] { nums[left], nums[right] }; } if (sum target) { left; } else { right--; } } return Array.Emptyint(); }为什么要用双指针而不是穷举因为数组排序之后指针移动是有方向的sum小于target时只有把左指针右移让和变大sum大于target时只有把右指针左移让和变小。每一轮至少排除掉一个元素所以最多走n步比两两组合的O(n^2)减少一个量级。这个写法的边界需要特别小心while条件必须是left right而不是left right否则会重复取到同一个元素返回空数组时调用方要能区分“无解”和“有解但值是0”的语义。我见过很多复盘报告WA原因最后都落在这种一两行的边界条件上所以宁可多写几行防御性判断也不要硬省。4. 我的每日刷题时间切片以及C#刷题翻车记录4.1 40分钟怎么切10分钟复习、15分钟新题、15分钟对照很多人听到“每日刷题”直觉反应是至少得一小时起步但我实测下来状态正常的40分钟完全够用。关键是这40分钟的结构必须固定不能想到哪做到哪。我按下面的表格分配时间时间段事项目的0-10分钟遮住答案重写前一天题目的核心解法用主动回忆对抗遗忘曲线10-25分钟做今日新题努力独立写出可运行解法训练从题目描述到代码的推导路径25-35分钟对照题解只研究更优解和边界处理避免收藏大量无用的解法35-40分钟在新题旁写一句“核心套路”总结把解题思路抽象成可复用的模式这里有个细节值得专门拿出来说复习要放在新题前面。很多人习惯先做新题等到快没时间了就“明天再复习旧题”而这个“明天”永远不会真正到来因为每天都有新的新题。把复习强制安排在最开始相当于给旧知识一个不可跳过的高优先级分片这是整个计划里最核心的制度设计。每天多出来这10分钟复习会让你的新题时间减少短期内看起来刷题速度变慢了但真实效果是旧题复现率大幅上升。我用这个方法试了三个星期原来标记为“会但忘”的一批题目大部分都能在一分钟之内回忆起思路。这种积累上的质变是单纯刷新题给不了的。4.2 值类型与可空值类型int?在记忆化搜索里的隐性开销刷题时经常用到记忆化搜索开一个缓存数组先判断某个状态算没算过算过就直接返回。很多C#选手会直接用可空类型int?[,] memo new int?[n, m];写起来很直观没有算过就是null算过就存值。但在大量递归场景里int?的每一次读取和赋值都附带额外的判空逻辑和装箱拆箱开销状态一多速度就下去了。更稳的做法是用普通int数组加一个“哨兵值”标记未计算int[,] memo new int[n, m]; const int UNCOMPUTED int.MinValue;初始化时把所有格子填成哨兵值访问时先判断memo[i, j] ! UNCOMPUTED。只要题目里的合法结果不可能等于那个哨兵值这种方式就既快又省心。这个优化看起来只有毫秒级的差别但在刷题平台上算法正确但常数太大也是会TLE的尤其是递归深度大、状态数量多的题目。我自己的习惯是能不用可空类型就不用给哨兵值取一个远离合法取值范围的数字然后在注释里写明为什么用这个值。4.3 LINQ顺手但惰性求值会让你TLEC#刷题最大的“爽点”之一就是LINQWhere一下Select一下Count一下代码简洁得赏心悦目。但LINQ的查询是惰性求值的也就是说Where返回的不是结果集而是一个“等真正枚举它的时候才执行的查询描述”。一个非常经典的翻车写法var evens nums.Where(x x % 2 0); int count evens.Count(); int total evens.Sum();这段代码看起来只做了一次筛选实际上Count遍历一次Sum又把整个查询重新执行了一遍等于把Where的谓词跑了两次。在数据量小的时候无所谓到百万级随机数据时就是实打实的耗时翻倍运气不好直接超时。我的经验是只要一个LINQ查询的结果会被使用两次以上就立即用ToList或ToArray物化var evens nums.Where(x x % 2 0).ToArray();物化之后后续的Count和Sum都是对已有数组的操作不再重复执行原始筛选逻辑。刷题时这个习惯非常重要因为题目的数据规模是设计好的测试用例不会因为你用了“看起来也没错”的写法就降低要求。空间换时间在算法题里是永远成立的。5. 一套轻量级调试框架让每日刷题从“赌运气”变“可复现”5.1 用固定seed的随机数据造样例比手编数据快得多每日刷题时最尴尬的一个瞬间是本地跑通了提交却WA然后你不知道去哪儿造一个能复现问题的数据。我之前手动编测试用例编到一半往往发现自己编的都是对称的“好看数据”根本触及不到边界。后来我固定了一套随机测试套路用Random类的固定种子生成数据这样每次跑出来的随机序列完全一致一旦发现错误用例下次启动还能原样复现。代码大概长这样static int[] GenArray(int n, int seed) { var rand new Random(seed); var arr new int[n]; for (int i 0; i n; i) { arr[i] rand.Next(-100, 101); } return arr; }为什么固定种子这么重要随机数生成器在给定相同种子的情况下会生成完全相同的数据序列这意味着你可以放心地用随机数据做大规模验证而不用担心“这次跑出错下台机器跑不出来”。对每日刷题来说你只需要记一个参数——数据规模和seed值就能随时重建出当时导致WA的测试环境。我用这个方法抓到过好几个奇怪边界比如数组全是0、全是负数、递增序列、递减序列等大概率手写用例根本想不起来的形态。每次跑出错误先把这个用例降到最小规模然后再一步步排查定位效率比肉眼扫代码高得多。5.2 空数组、单元素、全重复每组题都要过的边界清单刷题经验积累到一定程度之后我总结出了一份固定的边界检查清单。每次写完一道题不急着直接贴到LeetCode上先在本地用下面几类数据各跑一遍很多WA能提前拦截住。测试类型检查重点空数组代码会不会崩返回值是否合理单元素数组循环是否执行索引是否越界全部相同元素排序稳定性、去重逻辑、计数逻辑严格递增/递减指针移动方向、边界条件包含负数哨兵值的选取是否冲突极端最大值加法是否溢出int是否够用这份清单不是每道题都要全跑一遍它的作用是防止自己陷入“只挑看得顺眼的用例验证”的舒适区。很多时候题目的正确性就差在那一个扭着来的边界数据上而固定清单能让你用最小成本覆盖掉最常见的雷区。我现在每次刷完题都会在本地脚本里把这些用例跑一遍默认全部通过才提交提交通过的稳定性比以前高了很多。5.3 把刷题样例固化成一个本地测试脚本把边界清单和随机测试结合起来最终形成下面这样一个固定的测试骨架几乎可以套用到大多数独立函数型题目上using System.Diagnostics; class Program { static void Main() { Run(new[] { 1, 1, 2, 3 }, 3, 2, 基础示例); Run(Array.Emptyint(), 0, 0, 空数组); Run(new[] { 5 }, 5, 1, 单元素); Run(new[] { -1, -2, -3 }, -6, 1, 全负数); var rand new Random(2024); Console.WriteLine(开始随机测试...); Run(GenArray(2000, 2024), 10, -1, 量大随机); } static void Run(int[] nums, int k, int expected, string name) { int actual SubarraySum(nums, k); bool ok actual expected; Console.WriteLine(${name}: {(ok ? 通过 : 失败)}实际 {actual}); } static int[] GenArray(int n, int seed) { var rand new Random(seed); var arr new int[n]; for (int i 0; i n; i) arr[i] rand.Next(-10, 11); return arr; } static int SubarraySum(int[] nums, int k) { // 这里粘贴当天的解题函数 return 0; } }这个脚本的思路是把当天题目的函数复制进SubarraySum然后按自己总结的清单补充几组用例最后跑一次查看输出。它不需要任何第三方框架一个控制台项目就能跑。真正有价值的是白纸黑字的“expected”字段它逼着你在一开始就想清楚正确答案是什么而不是等着看程序输出再判断“好像差不多”。这套框架我用下来最大的体会是每天刷题后的收尾工作变得极其具象不再是一句模糊的“好像做对了”而是实打实的“这些用例全部通过”。如果你也被每日刷题搞得很累又看不到进步不妨先停掉盲目刷新题的冲动花一周时间把我这套复习、固定套路、本地调试三板斧完整跑一遍。等你再点开一周前做过的那道题直接在键盘上把解法敲出来的时候那种成就感比日历上再多一排绿格子要踏实得多。
延伸阅读

更多相关文章

2026/10/22 5:42:44

LoRa SX1268 C驱动解析:LoRaWAN协议栈接入与STM32移植避坑指南

简介:面向物联网嵌入式开发者的 LoRa 远距离通信资源,围绕 LoRaMac-node 开源协议栈,针对 Semtech SX1268 射频芯片提供完整 C 语言驱动与工程示例。代码已在设备端与服务器之间实现约 10 km 稳定通信,适合需要构建低功耗、广覆盖…

2026/10/22 5:37:44

Timexer+MKAN+PGA:时间序列预测优化的PyTorch实践

去年我接了一个设备温度场预测项目,数据是感器阵列的时序读数,外生变量不少,天气、负载、开关状态都有。基线我用的是 Timexer,公开 benchmark 上表现确实能打,尤其在多变量、多步预测场景下。但跑下来有个别扭的地方&…

2026/10/22 5:37:44

SolidWorks二次开发实测:插件与COM调用性能无显著差异

做过SolidWorks二次开发的人,几乎都纠结过同一个问题:这个工具到底写成插件(Add-in),还是写个独立程序,通过COM去调SolidWorks?网上的主流说法一贯是“插件在进程内加载,肯定快&…

2026/10/22 6:52:47

攻克SFFocusViewLayout布局难题:10个实战问题与完美解决方案

攻克SFFocusViewLayout布局难题:10个实战问题与完美解决方案 【免费下载链接】SFFocusViewLayout UICollectionViewLayout with focused content 项目地址: https://gitcode.com/gh_mirrors/sf/SFFocusViewLayout 引言:聚焦布局的痛点与解决方案 …

2026/10/21 4:53:08

金融AI落地指南:从调研报告到技术选型与工程实践

简介:这份PDF报告聚焦2025年金融服务业中国AI现状与趋势,面向金融科技从业者、AI战略规划人员及关注行业数字化转型的研究者,帮助读者把握生成式AI在银行、支付、交易等场景中的落地路径与投资方向。资源包内含1个PDF文件,大小约1…

2026/10/22 0:01:49

电商智能运营体系从0到1:架构设计、落地步骤与避坑指南

前两年我们团队接了一个电商平台的运营中台项目,目标很直白:把过去靠运营拍脑袋做的选品、定价、发券、推活动这些事,逐步用数据算法和自动化流程代替。项目名字内部就叫“电商智能运营体系”。最近有不少朋友问我当时是怎么设计的、踩过哪些…

2026/10/22 0:01:49

若依框架AI代码生成不稳定?用Agent Skill三层结构+五步法搞定

最近一段时间,我一直想把后台管理系统里那些机械重复的增删改查代码交给AI去写。框架用的是若依,按理说这类业务代码模式非常固定,应该是AI最擅长的事情。可真试下来却不是那么回事——你让AI生成一个用户管理模块,它给出的Contro…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑