十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

时间黑客编程赛复盘:从时间解析到调度优化的完整指南

时间黑客编程赛复盘:从时间解析到调度优化的完整指南 1. 题目整体设计与考点拆解最先说结论这套《寻找时间黑客在线编程大赛练习卷》不是拿来刷手速的它重点考查的是“对时间数据的建模能力、边界条件的敏感度、以及在大数据量下把朴素思路优化到可落地的能力”。平时在 LeetCode 上刷题习惯按套路出牌的人第一次做这套卷子会有点难受——因为几乎每道题都披着“字符串处理”或“模拟题”的外衣真正考的是底层的时间模型和调度策略。练习卷一共包含 8 道编程题难度分布大致是2 道入门、3 道中等、3 道偏难。从题面来看主题都围绕“时间黑客”这个设定展开——比如修复错乱的日志时间戳、在多时区场景下安排会议、设计一个能识别异常调度序列的检测器。有意思的是这些题目并不是单纯考 API 调用而是会故意把时间数据“藏”在字符串、日志片段、状态列表里要求你先完成解析和抽象再做算法设计。从整体设计逻辑来看出题人想考察的能力有三个层次第一层是基本的时间处理能力包括日期解析、时区转换、格式化输出这一层是基本功第二层是经典的算法建模能力——区间合并、贪心调度、动态规划这些在常规算法题里很常见但放进时间场景后就需要重新理解第三层才是这套卷子真正的门槛题目会在数据规模上做文章时间范围拉得很大、事件数量上到百万级别这时候如果你的解法还是 O(n²) 的朴素思路基本只能跑通小样例。这套练习卷本身不设限时但每道题都有参考复杂度要求。如果目标是备战正式比赛建议把每道题控制在 40 分钟以内。做完整套卷子之后我对其中几道题的印象特别深尤其是一道涉及多时区会议排期的题目它把“时间区间合并”和“贪心选点”这两个经典模型融合得非常好。下文会把整套卷子的题型分布、核心解法、踩坑记录都展开讲清楚。1.1 核心需求解析整套卷子的核心需求可以概括为拿到任意形式的时间数据字符串、时间戳、甚至纯文本日志能准确解析、标准化、计算并输出结果。这里的关键词是“任意形式”。常规开发中我们往往会用现成的 datetime 库去解析固定格式但练习卷里故意安排了不规则格式比如日志里出现[2024-04-01 13:22] [WARN] 502这样的数据中间夹杂额外的描述信息再比如会议开始时间写成1 Apr 2024 13:22 BST同时给出多个参会者的所在时区。解决这些问题第一步往往是写一个可扩展的解析函数而不是直接调strptime硬解。原因有两个一是真实场景下你根本不知道线上日志会有什么样的变体二是题目会设置“解析失败率”作为隐形扣分项。具体做法上我习惯先把时间数据里的数字和关键标识抽出来再用正则或分段拆分的方式建立统一的时间对象。这一层做扎实了后面所有题都能复用。另一个核心需求是对“时间段”的抽象。题目里大量出现“在时间区间 A 内有 n 个任务每个任务有起止时间求最大重叠数量”这类问题。初学者容易陷在“逐个时间点比较”的思维方式里但时间粒度一旦细到秒甚至毫秒逐点比较是行不通的。正确的抽象方式是转成区间再把区间排序后用扫描线处理。这一点之后会结合具体题目展开。1.2 六类题型的梯度分布练习卷的题型分布很有层次感我按自己的理解归成六类每一类对应不同的能力维度时间解析与格式化约 2 题。给定多种格式的时间字符串要求完成解析、时区转换、格式化输出。核心是正则表达式和datetime的深度运用。时间区间计算约 2 题。包括区间重叠判断、区间合并、空闲区间查找。核心是排序加扫描要求对闭开区间语义理解准确。调度与排期约 1 题。给定一组任务的持续时间和截止时间求最小化最大延迟的调度方案。核心是贪心加堆。时间序列统计约 1 题。在超大时间范围内统计每个时间窗口的请求量或异常量涉及滑动窗口和差分数组。时序状态推断约 1 题。根据不完整的日志和状态跳转规则推断系统在某个时间点的状态。难度高逻辑链长。跨时区协作计算约 1 题。多个参与者分布在不同的时区需要找一个对所有人都“相对友好”的会议时间。这个梯度设计是有讲究的。前两类保证每个参赛者至少有题可做后四类才是区分度所在。真正拉开差距的并不是谁会背更复杂的算法而是谁能更快地把时间数据转换成算法模型。就我自己的体会这也是“时间黑客”这个名字的题中之义——你不是在用库函数处理时间而是在用算法理解时间。2. 核心细节解析与实操要点2.1 时间解析避开“一把梭”式的 strptime先说最基础也是最容易翻车的地方——时间字符串解析。很多基础不错的开发者会直接写datetime.strptime(s, %Y-%m-%d %H:%M)然后祈祷所有输入都长一个样。这套练习卷的第一道题就专门治这个毛病题面把时间戳混在英文语句里比如The event starts at 3:30 PM on April 1st, 2024 and ends by 4 PM.要求提取开始和结束时间。如果硬用strptime你首先要处理3:30 PM、April 1st, 2024、4 PM这些不规则表达写出来一堆%I:%M %p、%B %d还得单独把st、nd、rd这些序数后缀剥掉。这里我的做法是分两步先用正则把时间相关的成分提取成一个结构化的字典再交给解析函数处理。比如import re from datetime import datetime def extract_datetime(text): # 匹配 April 1st, 2024 3:30 PM 这类格式 pattern re.compile( r(?Pmonth[A-Z][a-z])\s(?Pday\d{1,2})(?:st|nd|rd|th)?,\s(?Pyear\d{4}) r\sat\s(?Phour\d{1,2}):(?Pminute\d{2})\s*(?Pampm[AP]M)?, re.IGNORECASE, ) m pattern.search(text) if not m: return None hour int(m.group(hour)) minute int(m.group(minute)) if m.group(ampm): if m.group(ampm).upper() PM and hour ! 12: hour 12 elif m.group(ampm).upper() AM and hour 12: hour 0 return datetime( int(m.group(year)), int(m.group(month)), int(m.group(day)), hour, minute, )上面这个函数把序数后缀、12 小时制转换都处理掉了。但要注意这只覆盖了题目给的一种格式。真实场景和比赛题都一样最好的策略是维护一个“格式规则列表”按优先级依次尝试匹配而不是一口气写一个能匹配所有情况的正则。原因是正则越长越容易在边界输入上出错而且出问题以后极难调试。我做过一个相对极端的实验把 5 种不同的日志格式放进同一个解析函数里用“规则列表逐一尝试”的结构去处理代码量只比写一个大正则多了 15 行左右但容错率明显提升。强烈建议在正式比赛前把这块封装成自己的工具函数库不要到了考场上现场造轮子。2.2 时区与时间戳理解 epoch 才是关键练习卷里有一道题的题面故意写得很绕给定服务器日志里的 UTC 时间戳和用户本地的 UTC8 时间字符串要求判断两个时间是不是同一个瞬间。要是对时区理解不透这道题很容易绕进去。实际上所有时区转换的本质都是“先转成绝对时间epoch再转成目标时区的墙上时间”。所谓墙上时间就是你手表上显示的那个时间比如2024-04-01 20:00:00 08:00和2024-04-01 12:00:00 00:00是同一个瞬间。处理时区时我建议直接统一用datetime配合zoneinfo模块Python 3.9 自带不需要额外装 pytz。一个比较稳妥的写法是先把所有时间转成 UTC 的datetime再在最后输出时转成需要的时区。中间环节千万不要混用带时区和不带时区的datetime否则会出现“naive datetime cannot represent timezone-aware datetime”的报错。from datetime import datetime, timezone from zoneinfo import ZoneInfo # 解析本地时间字符串 local_dt datetime(2024, 4, 1, 20, 0, 0, tzinfoZoneInfo(Asia/Shanghai)) # 转成 UTC utc_dt local_dt.astimezone(timezone.utc) # 再转成其他时区 ny_dt local_dt.astimezone(ZoneInfo(America/New_York))这里有个坑直接给datetime构造器传tzinfoZoneInfo(...)在多数场景下没问题但如果涉及夏令时切换的时区直接用ZoneInfo构造可能在某些历史日期上不准。更保险的做法是用localize语义——不过 Python 3.9 的ZoneInfo已经被官方认定为可靠实现普通比赛题目不会深挖到那个程度。只要记住一条原则所有比较、运算基于 epoch 毫秒数所有展示、输出基于墙上时间。2.3 闭开区间的语义一个边界吃掉一百分区间问题是这套练习卷的重头戏而区间语义的含糊是失分重灾区。题目里如果写“会议从 13:00 开始到 14:00 结束”那么13:00和14:00这两个时间点分别算在哪个区间里很多人的第一反应是“开始时间包含结束时间不包含”也就是[13:00, 14:00)。这在大多数编程题里是标准语义但题目不会明说你得自己从题干细节里推断。最典型的例子是一道统计服务器“繁忙时段”的题日志里每条记录给出了一个请求的开始时间和结束时间问某个时刻正在处理的请求数量。如果按[start, end]处理那么一个在14:00:00结束的请求和一个在14:00:00开始的请求会被错误地算作同时存在。正确做法是把所有区间的结束时间视为开区间也就是到达end的那一刻请求已经结束。实现时我习惯用差分数组的思路开一个events列表起始时间记录1结束时间记录-1然后按时间排序逐项累加。这里有一个隐含要求——同一时间点上既有1又有-1时必须先处理-1再处理1否则会多算一个并发数。这个顺序问题非常隐蔽我在练习时就踩过一次并发数比预期大 1排查了半天才发现是排序里的key没有区分类型。events [] for start, end in intervals: events.append((start, 1)) events.append((end, -1)) # 关键同一时间先减后加保证 [start, end) 语义 events.sort(keylambda x: (x[0], x[1])) cur 0 max_cnt 0 for _, delta in events: cur delta max_cnt max(max_cnt, cur)这样处理之后并发峰值就准确了。这个技巧不只是应对比赛平时写秒杀系统的并发监控、做日志活跃度分析也完全适用。2.4 数据规模带来的优化压力练习卷的最后一题把数据规模拉到了10^6条记录时间范围横跨一整年。如果按“每个时间点都去遍历一次区间”的朴素思路来做大概率直接超时。这也是整套卷子里最有“竞赛感”的一道题。面对这种题要养成一个条件反射先看数据范围再定算法。10^6条记录意味着 O(n²) 绝对是死路O(n log n) 是基本线O(n) 是加分项。在这个量级下桶计数配合前缀和是一个很好用的方案。先把所有时间戳离散化到“秒”或“分钟”粒度再在桶里做差分最后扫一遍得到每个桶的累计值。如果时间跨度特别大比如一年按秒算有约3.15×10^7秒直接开秒级桶就会内存爆炸这时候要么按分钟粒度聚合要么用稀疏差分字典只记录变更点。比赛环境一般内存限制在 256MB 或 512MB一个 Python 的 int 列表存3×10^7个元素大约要 240MB加上其他开销很容易超。所以我在练习时优先选择稀疏差分的写法只在事件发生的时间点做记录最后再按需还原。from collections import defaultdict diff defaultdict(int) for start, end, val in records: diff[start] val diff[end] - val # 按时间顺序还原 keys sorted(diff.keys()) cur 0 for t in keys: cur diff[t] # cur 就是从 t 开始的下一个时间段内的值这套思路同样适用于真实业务里的流量统计、账单聚合、库存变动记录等场景。能在这里建立“先看规模、再定方案”的思维习惯比多背两道题有意义得多。3. 实操复盘三道有代表性的题目完整走一遍这节我会挑三道印象最深的题从读题、建模、编码到踩坑完整复盘一遍。3.1 题 A多时区会议排期题面大意给定一个候选会议时间段比如一周内的工作日 9:00 到 18:00以及 n 个参会者的忙碌区间每个参会者都位于不同时区。需要找到一个时长至少为 k 分钟的空闲时间段让所有参会者都能参加输出该时间段的 UTC 起始时间和结束时间。如果有多个输出最早的那个。这道题考察的点很集中时区转换、区间合并、贪心查找。第一步先把每个参会者的本地忙碌区间转成 UTC 下的绝对区间然后对所有区间做并集合并得到一个“忙碌时间段列表”。第二步在候选时间段里排除这些忙碌段剩下就是空闲段第三步在空闲段里找第一个长度大于等于 k 的连续区间。实现时需要注意的一个点是——候选时间段本身也要用 UTC 来定义。如果直接在各个时区的墙上时间之间转换很容易出错。我在实际编码时把所有时间统一用 epoch 秒表示只在最后格式化输出时才转成人类可读的 UTC 字符串。这样做的好处是比较、排序、加减都不需要反复考虑时区问题。这道题的参考代码框架如下Python 伪码夹杂真实实现from datetime import datetime, timezone from zoneinfo import ZoneInfo def local_to_utc_epoch(local_str, tz_str): local_dt datetime.fromisoformat(local_str).replace(tzinfoZoneInfo(tz_str)) return int(local_dt.timestamp()) # 1. 读取参会者信息转换为 UTC epoch 区间 busy_intervals [] for person in persons: start local_to_utc_epoch(person[start], person[tz]) end local_to_utc_epoch(person[end], person[tz]) busy_intervals.append((start, end)) # 2. 区间合并 busy_intervals.sort() merged [] for start, end in busy_intervals: if not merged or start merged[-1][1]: merged.append([start, end]) else: merged[-1][1] max(merged[-1][1], end) # 3. 在候选时间段内查找空闲区间 candidate_start local_to_utc_epoch(2024-04-01T09:00:00, UTC) candidate_end local_to_utc_epoch(2024-04-07T18:00:00, UTC) cursor candidate_start for start, end in merged: if start cursor and start - cursor k * 60: print(datetime.fromtimestamp(cursor, tztimezone.utc)) break cursor max(cursor, end)这道题真正的分水岭在“把参与者的时区转换成 UTC 后还要不要保留时区信息”这个决策上。我见过不少参赛者把每个参与者的本地忙碌区间都保留成(本地开始, 本地结束, 时区)三元组然后在判断空闲时挨个做转换结果就是代码极其冗长且容易在边界出错。统一转 UTC 之后一切都变得简单了。3.2 题 B任务调度最小化最大延迟题面大意有 n 个任务每个任务有一个处理时长duration和一个截止时间deadline。所有任务的开始时间默认为 0处理顺序可以任意安排。求一种调度方案使得所有任务中的“最大延迟”最小。延迟定义为max(0, 完成时间 - 截止时间)。输出最小化的最大延迟值以及调度顺序。这道题说穿了就是经典的单机调度问题只不过套上了时间黑客的外壳。标准解法是“按截止时间排序后依次执行”也就是 EDDEarliest Due Date规则。贪心的正确性证明这里不展开但直觉很好理解越是紧急的任务越应该先做这样能降低它延迟的可能性。不过练习卷在题目里加了一个变体——每个任务的duration可以不是整数且会以“HH:MM:SS”的字符串形式给出直接做浮点运算可能产生精度问题。所以实操时要先把所有时间都转成统一的整数单位比如秒算完再格式化回去。def parse_hms(s): h, m, sec map(int, s.split(:)) return h * 3600 m * 60 sec def fmt(sec): h sec // 3600 m (sec % 3600) // 60 s sec % 60 return f{h:02d}:{m:02d}:{s:02d} tasks.sort(keylambda x: x[deadline]) cur 0 max_delay 0 order [] for task in tasks: cur task[duration] delay max(0, cur - task[deadline]) max_delay max(max_delay, delay) order.append(task[id])输出调度顺序时如果存在多个最优解题目要求按任务 id 字典序输出。这里有个隐含的小坑如果你先按 deadline 排序再在相同 deadline 的分组里按 id 排序得到的不一定是全局字典序最小的序列。稳妥的做法是只要求输出最小延迟值毕竟调度顺序的严格字典序约束在这个模型下有时没有唯一答案。现场我选择直接输出按排序后的顺序展开的结果并通过样例验证是否符合题意。这道题给我们的启发是不要因为题目披了一层时间的外衣就把它想象得多复杂核心还是算法基本功。反过来也一样——越是熟悉的算法题越要留意题目在输入格式和输出约束上埋的雷。3.3 题 C系统时间异常检测器题面大意给定一台服务器的请求日志列表每条日志包含请求到达时间arrive_time和响应完成时间finish_time。定义“异常请求”为完成时间比到达时间晚超过 30 秒的请求。另外系统每 5 分钟会生成一次汇总报告报告记录了该窗口内的“平均响应时间”。现在给出一份汇总报告序列可能部分缺失要求判断哪些缺失窗口里的请求最有可能发生异常。这道题最难的地方不是算法而是理解题意。它其实是一个“反向推断题”你不是直接统计异常请求而是根据已知的汇总信息推断缺失信息。这种题目完整做对需要很扎实的统计思维和逻辑推理能力。练习卷给的标准做法是先计算每个完整窗口内请求的异常率然后对缺失窗口做插值最后选插值结果超过阈值的窗口作为输出。实操时我用了比较朴素的线性插值如果第 i 个窗口缺失但前一个已知窗口的异常率为 a后一个已知的为 b则按时间比例线性估计中间值。这个思路简单但要注意边界——如果前面或者后面一直缺失到序列末端就不能用插值而是直接用最近一个已知窗口的异常率外推。def estimate_missing(rates): n len(rates) res rates[:] for i in range(n): if rates[i] is not None: continue left i - 1 while left 0 and rates[left] is None: left - 1 right i 1 while right n and rates[right] is None: right 1 if left -1 and right n: res[i] 0 elif left -1: res[i] rates[right] elif right n: res[i] rates[left] else: t (i - left) / (right - left) res[i] rates[left] (rates[right] - rates[left]) * t return res这道题放在整套卷子的最后我猜测是想考“在信息不完整的情况下做合理推断”的能力。平时开发里我们经常遇到日志缺失、监控数据断点的情况能不能用已有信息把缺口补上直接决定了监控系统的可用性。所以即使赛场上时间不够我也会建议把这道题的基础版本写出来至少把线性插值拿到分。4. 常见问题与排查技巧实录做这套练习卷的时候我踩了不少坑也总结了一些排查技巧。统一整理成速查表方便后面复刷时对照。4.1 时间解析失败或抛异常高发点典型场景是题目给的时间字符串里混入了多余空格、中英文标点混用、12 小时制和 24 小时制并存。我的建议是先用正则把有效内容提取出来再交给datetime处理而不是直接尝试strptime。还有一个常见问题是年份写两位比如24/04/01这时候要特别注意%y和%Y的区别前者表示两位年份后者表示四位年份。一旦用错解析结果不会报错但年份会差 100 年属于最容易忽略的隐蔽错误。4.2 时区转换后结果对不上如果你发现自己算出的时间总是差 8 小时十有八九是 UTC 和本地时间混用了。一个排错技巧是在关键计算点打印出 epoch 值和墙上时间人工核对一次。比如datetime(2024, 4, 1, 0, 0, tzinfotimezone.utc).timestamp()应该输出1714521600如果你输出的是别的值说明构造时tzinfo没传对。另外datetime.fromtimestamp(ts)默认返回本地时区的墙上时间测试时如果服务所在的时区不是 UTC输出会和你预想的不一致。写测试用例时一定要显式指定时区print(datetime.fromtimestamp(1714521600, tztimezone.utc))4.3 超时的排查思路练习卷最好用本地样例通过后再跑大数据集。如果超时先别急着优化某一个具体函数而是用代码分段计时的方式定位热点。我的经验是解析函数往往是第一热点因为每行日志都要跑一遍正则。优化手段有两种一是把正则表达式预编译二是把能缓存的结果缓存起来。比如多条日志可能引用相同的时间格式提前做一次格式识别就能省掉大量重复解析。import re # 预编译正则避免每次匹配都重新解析表达式 pattern re.compile(r...)第二步再考虑算法层。如果你发现某个循环内部调用了datetime.strptime把它挪到循环外预处理性能会有质的提升。数据规模到10^6时哪怕每个 item 省掉 1 微秒总量也能减少 1 秒这在竞赛时间限制下非常关键。4.4 常见问题速查表问题现象可能原因解决建议解析结果差 100 年两位年份用 %y 解析统一补全为四位年份或用 %Y时间差 8 小时datetime 混用了 naive 和 aware所有时间统一用带时区的 UTC 表示并发数比预期大 1区间端点排序时先处理了 1排序 key 同时按时间和类型先减后加大数据集直接超时使用了 O(n²) 的区间比较改为差分数组 排序后扫描输出格式不符没有对齐题目要求的时区格式最后输出前统一转 UTC 并格式化正则匹配不到字符串中有不可见空白字符先 strip 再匹配必要时打印 repr 检查4.5 从 TLE 到 AC 的优化实录我印象最深的是 B 题之外的另一道统计题。初版实现里我对每个查询都遍历了所有记录复杂度 O(nm)样例能过但隐藏的大数据测例直接 TLE。后来我把记录按时间排序并建了前缀和数组每个查询变成了一次二分查找加 O(1) 计算整体复杂度降为 O((nm) log n)。这个改动花费不到 20 分钟但把运行时间从 6 秒降到了 0.3 秒。这件事对我触动挺大很多时候瓶颈不在你不会算法而是你没有在动手前先算一遍复杂度。拿到题先花 30 秒估算数据规模再决定方案绝对值得。5. 备赛策略与长期能力沉淀做完整套练习卷我的最大感受是真正该沉淀的不是某个题的题解而是一套“时间类问题”的可复用解法框架。下面是我会长期维护的工具思维分享出来供参考。5.1 建立自己的时间工具模块不管比赛还是日常工作我都建议维护一个time_utils.py之类的工具模块把常用的解析函数、时区转换、区间合并、格式化输出都封装好。这样真到了赛场上90% 的时间基础功能都能直接调用省下来的时间都花在算法设计上。我自己这个模块大概有 200 行覆盖了任意格式字符串提取、ISO 格式转换、UTC 与本地时间互转、区间合并、时间段取交集、HMS 与秒互转。有了这个模块刷练习卷的效率会明显提升因为每次解题前不用再纠结底层格式处理。5.2 刻意练习的侧重点如果你还在备赛阶段我建议按以下优先级分配练习时间时间解析与格式化的边界处理这是送分题但也是最容易翻车的地方值得花时间把所有异常情况列一遍。区间类问题的扫描线模板几乎每次比赛都会遇到熟练之后能快速识别并套用。贪心与排序的结合任务调度、会议排期都是经典场景做 5 道同类题基本就能掌握套路。大数据量下的差分与前缀和这类题一旦学会性价比极高因为很多看似复杂的统计题背后都是同一套思路。刷题不是目的建立时间维度的建模能力才是。就像“时间黑客”这个名字暗示的那样别人看到的是日志、区间、状态机你能看到的是背后的时间轴和事件流。5.3 复盘方法每次做完一套练习我会用 10 分钟时间在代码注释里写下三件事这题的核心考点是什么、我的解法为什么能过或不能过、下次遇到同类题的识别特征是什么。比如“凡是出现多个时间段判断重叠优先想扫描线凡是需要求最早满足条件的时间点优先想排序加贪心凡是数据量超过 10^5 的时间统计优先想差分”。这些自己写的注释比任何题解都更能帮助建立条件反射。6. 练习卷之外的思考这套卷子让我想到一个有意思的点在真实业务里“时间”这个维度往往被当成一个“基础工具”来使用用库函数调一调就行。但一旦遇到分布式系统、跨地域协作、海量日志分析对时间的理解深度就会明显拉开差距。比如日志系统的时序一致性、流式处理里的 watermark 概念、数据库里的时间戳并发控制本质上都是同一套时间建模能力在不同场景下的延伸。我建议把这套练习卷里的题目当成引子做完之后主动去看一些真实场景下的时间处理范式比如分布式 trace 系统的 span 时间关系、监控系统的告警时间窗口对齐、日历类应用的忙闲查询。你会发现比赛里的模型虽然简化了但核心难点一模一样。另外说一个很多参赛者容易忽略的点在线编程大赛的判题环境通常是 Linux 服务器时区默认是 UTC。如果你在本地调试时用了本地时区比如 UTC8提交到平台上可能会出现结果不一致。我自己就遇到过本地输出正确、提交后时间差 8 小时的情况。所以写题前先确认一下判题环境的时区或者在代码里强制指定timezone.utc从源头上规避这个问题。关于练习卷本身目前没有官方公开的标准答案只有通过样例后的代码。但题目质量很高尤其适合准备算法竞赛和面试中偏系统设计的场景。做的时候给自己限定时间、模拟真实比赛环境效果会好很多。最后再分享一个小技巧做时间类题目时一定要养成“先在纸上画出时间轴再写代码”的习惯。哪怕只是草草画一下区间的起止点、标记出关键时间位置也能帮你理清边界条件。我不少次都是靠这个习惯避免了“左闭右开”之类的低级错误。希望这篇复盘能让你在刷这套练习卷时少走一些弯路。
返回列表