题目集1~3的总结性Blog
一·前言
1.第一次题目集(题量:5;难度:偏简单)
7-1 身份证号校验位
核心知识点:
身份证校验位算法(加权求和(前 17 位分别乘对应权重)、模 11 取余、根据余数映射校验位(0-10,10 对应 X));
分支逻辑处理(校验位的映射规则);
数值计算与类型转换。
7-2 求解一元二次方程(类设计)
核心知识点:
类的设计与封装(属性私有化、提供 getter/setter 方法);
构造方法的定义与使用(初始化方程系数 a、b、c);
成员方法的设计(求解判别式、计算根(实根 / 复根)、返回结果);
面向对象的逻辑拆分(将 “方程数据” 与 “求解行为” 封装为类,体现高内聚)。
7-3 验证码校验、7-4 QQ 号校验
核心知识点:
正则表达式语法规则(字符匹配、量词、边界符、字符类等);
场景化正则设计:
验证码校验(如字母 + 数字组合、固定长度、大小写限制等);
QQ 号校验(开头非 0、长度范围(如 5-13 位)、纯数字匹配);
Java 中正则的应用(Pattern类、Matcher类、String.matches()方法);
字符串合法性校验逻辑(结合正则实现输入数据的有效性判断)。
7-5 单部电梯调度程序
核心知识点:
业务需求拆解(电梯的核心状态:当前楼层、运行方向、待处理请求队列;核心逻辑:请求响应优先级、上下行调度规则、门开关控制等);
状态管理(电梯运行状态(停 / 上行 / 下行)、请求队列的增删改查);
调度算法设计(如 “就近原则”“同向优先”“先到先服务” 等调度策略);
多场景分支处理(如电梯在运行中接收新请求、无请求时的待机逻辑、到达目标楼层后的停靠与开门逻辑);
代码结构设计(如拆分电梯类、请求类、调度器类,体现模块化)。
2.第二次题目集(题量:3;难度:偏中等)
知识点:
a.类的设计与封装
属性私有化:将对象的状态(如点的坐标、雨刷的角度、电梯的当前楼层)封装为私有成员变量(private)。
方法公有化:通过公共的 getter/setter 方法或行为方法(如 move(), draw(), operate())来访问和修改对象的状态,确保数据的安全性和一致性。
核心思想:隐藏对象的内部实现细节,只暴露必要的接口供外部交互。
b.构造方法 (Constructor)
用于初始化对象的成员变量。
题目会考察带参数的构造方法,以确保对象在创建时就具备合理的初始状态。
c.方法的设计与实现
行为方法:根据题目要求,设计完成特定功能的方法(如计算两点距离、计算雨刷扫过的面积、处理电梯请求)。
参数与返回值:正确设计方法的输入参数和返回值类型,确保方法的可用性和通用性。
代码复用:可能需要通过组合(Has-A)或继承(Is-A)来复用代码,但根据题目描述,组合的可能性更大。
3.第三次题目集(题量:3;难度:偏中等)
知识点:
7-1 NCHU_销售步枪问题
知识点:
a.类的设计与封装:
设计Rifle类:封装步枪的属性,如型号、单价、库存数量等。
设计Customer类:封装顾客的属性,如年龄、购买资格(是否有许可证)等。
b.类的行为与交互:
在Rifle类或一个Store类中设计销售方法(如sellTo(Customer customer))。
方法内部实现业务规则校验逻辑:
检查顾客年龄是否达标。
检查顾客是否持有有效许可证。
检查步枪库存是否充足。
c.条件判断与流程控制
使用if-else或switch语句实现复杂的业务规则判断,并根据判断结果执行不同的操作(如完成销售、提示错误信息)。
d.状态修改
当销售成功时,正确修改相关对象的状态(如减少步枪的库存)。
7-2 NCHU_蒙特卡罗方法求圆周率
知识点:
算法思想:理解蒙特卡罗方法的核心 ——用随机性解决确定性问题。
几何概率模型
随机数生成:使用 Java 的Random类或ThreadLocalRandom生成指定范围内的随机浮点数。
循环与统计
7-3 NCHU_单部电梯调度程序(类设计 - 迭代)
知识点:
a.系统分析与类设计:
Elevator类:封装电梯的核心状态和行为。
状态:当前楼层、运行方向(上 / 下 / 停)、门状态(开 / 关)、载重等。
行为:moveUp(), moveDown(), openDoor(), closeDoor(), addRequest(), processRequests()等。
Request类:封装一个电梯请求,包括请求的楼层、请求的方向(上 / 下)、请求时间等。
b.状态机管理
电梯的行为严格依赖于其当前状态。例如,只有当电梯停止且门关闭时,才能开始上行或下行。
c.请求队列与事件驱动
设计一个数据结构(如List或Queue)来管理所有待处理的请求。
当外部按下按钮时,生成一个Request对象并加入队列。
d.调度算法实现:
需要设计一个算法来决定电梯的下一个目标楼层。
SCAN(扫描算法 / 电梯算法)
LOOK
二·设计与分析
第一次题目集(单部电梯调度程序)

1. 面向对象设计的 “抽象” 与 “封装”
在设计这个电梯系统时,将现实中的电梯实体抽象为 ElevatorSimulation 类,把 “楼层范围”“当前位置”“运行方向”“运行状态” 等属性封装为类的成员变量,同时将 “处理请求”“调度方向”“移动楼层” 等行为封装为类的方法。这种封装让代码的职责更清晰,例如外部代码只需调用 handleRequest() 方法即可添加请求,无需关心内部如何存储和处理。
而 Direction 和 ElevatorState 枚举的设计,是对 “电梯方向” 和 “电梯状态” 这两个有限且固定的概念的抽象,既避免了使用字符串或整数带来的类型歧义,又通过枚举值的约束保证了逻辑的合法性。
2. 数据结构的选择与业务逻辑的契合
对于内部请求,使用 Queue(队列)是因为内部请求是 “乘客按下目标楼层” 的行为,遵循 “先按先处理” 的逻辑,队列的 FIFO 特性正好匹配。
对于外部上行 / 下行请求,使用 SortedSet(有序集合)是因为同方向的外部请求需要 “按楼层顺序处理”。TreeSet 自动排序的特性可以帮我们省去手动排序的麻烦,同时保证了请求的唯一性(同一楼层同一方向的重复请求会被自动去重)。
3. 业务规则的落地:“同方向优先” 调度逻辑
题目要求 “电梯向某个方向移动时,优先处理同方向的请求,处理完后再处理相反方向的请求”。为了实现这一点,在 scheduleNextMove() 方法中做了三层判断:
若电梯已有运行方向,先检查同方向是否还有未处理的请求;
若同方向无请求,再切换方向检查反方向的请求;
若初始无方向,则优先选择 “请求更靠近当前楼层” 的方向。
这种逻辑确保了电梯不会 “来回跑”,符合现实中电梯的调度习惯,也提升了运行效率。
第二次题目集(单部电梯调度程序(类设计))

1. 模块化设计:“单一职责” 的体现
这个类图将系统拆分为实体层(Elevator、ExternalRequest)、控制层(Controller)、数据层(RequestQueue)和枚举约束层(Direction、State),每个模块只负责一类职责:
Elevator 只关心自身的属性和基础行为(移动、开关门);
RequestQueue 只关心请求的存储与管理;
Controller 只关心调度逻辑(方向决策、请求处理时机)。
这种拆分让代码更易维护,比如修改调度规则时,只需调整Controller,不影响Elevator的基础逻辑。
2. 面向对象的 “关联” 与 “依赖”
Elevator 与 Direction、State 是关联关系(电梯的方向和状态由枚举约束),确保了属性的合法性。
Controller 与 Elevator、RequestQueue 是组合关系(控制器依赖电梯和请求队列才能工作),体现了 “整体 - 部分” 的逻辑。
RequestQueue 与 ExternalRequest 是聚合关系(请求队列包含多个外部请求),明确了数据的组织方式。
这些关系的设计让类之间的交互清晰可追溯,符合面向对象设计的 “低耦合、高内聚” 原则。
3. 枚举的约束价值
Direction 和 State 枚举的使用,是对 “有限状态” 的精准抽象:
电梯的方向只能是 “上、下、空闲”,状态只能是 “移动、停止”,避免了用字符串或整数带来的歧义(比如误将 “LEFT” 作为方向)。
枚举的 “常量性” 也让代码的可读性大幅提升,比如 elevator.setDirection(Direction.UP) 比 elevator.setDirection(1) 更直观。
4. 调度逻辑的封装(Controller 的作用)
电梯的核心调度规则(“同方向优先”“停靠处理请求”)都封装在Controller中:
determineDirection 方法负责决策下一个运行方向,确保优先处理同方向请求;
shouldStop 方法判断当前楼层是否需要停靠,整合了 “内部目标楼层” 和 “外部同方向请求楼层” 的校验;
processRequests 方法串联起 “方向决策→移动→停靠→处理请求” 的完整流程。
这种封装让业务规则与电梯实体解耦,便于后续扩展(如添加多电梯调度、优先级请求)。
第三次题目集(单部电梯调度程序(类设计-迭代)

1. 领域模型的精细化:“乘客(Passenger)” 的抽象
与之前的设计相比,这个类图引入了 Passenger类 ,将 “内部请求” 和 “外部请求” 统一为 “乘客的行程请求”。这种抽象的优势在于:
内部请求(乘客在轿厢内按目标楼层)和外部请求(乘客在楼层外按上行 / 下行)本质都是 “乘客从某层到某层的行程”,用Passenger类封装后,模型更统一、语义更明确。
Passenger的getDirection方法可以自动计算乘梯方向(根据出发和目标楼层),减少了外部逻辑的复杂度。
2. 模块化设计的进阶:“高内聚、低耦合” 的强化
Elevator类只关注自身的属性(楼层、方向、状态)和基础行为(移动、开关门),不关心请求从何而来。
RequestQueue类只关注请求的存储与管理,不关心电梯如何处理这些请求。
Controller类作为 “中介”,仅负责协调电梯和请求队列的交互,不侵入电梯或请求队列的内部逻辑。
这种拆分让每个模块的职责更纯粹,比如修改调度算法时,只需调整Controller,不会影响Elevator的基础运行逻辑。
3. 枚举的约束价值深化
Direction和State枚举不仅约束了电梯的状态,也约束了乘客请求的方向(Passenger的getDirection返回Direction枚举值)。这种枚举在多模块中的一致性使用,确保了整个系统的类型安全,避免了因 “方向字符串大小写”“状态整数歧义” 带来的 bug。
4. 调度逻辑的扩展性
Controller中的determineDirection、shouldStop、getClosest等方法,是调度规则的核心载体。如果需要扩展功能(如添加 “电梯满载不响应新请求”“优先级乘客优先处理”),只需在Controller中新增或修改这些方法,无需改动Elevator或Passenger的核心逻辑,体现了设计的扩展性。
三·采坑心得
(电梯程序实验没有通过测试点)
输入:
1
20
3,UP
<5>
<6,DOWN>
<7>
<3>
end
运行测试(与实际运行结果不符):
Current Floor: 1 Direction: UP
Current Floor: 2 Direction: UP
Current Floor: 3 Direction: UP
Open Door # Floor 3
Close Door
Current Floor: 4 Direction: UP
Current Floor: 5 Direction: UP
Open Door # Floor 5
Close Door
Current Floor: 6 Direction: UP
Current Floor: 7 Direction: UP
Open Door # Floor 7
Close Door
对比实际运行结果和预期结果,发现程序缺少了 “反向处理请求” 的逻辑。
具体来说:
当电梯上行处理完所有同向请求后(到达 7 层),没有切换为下行方向去处理6,DOWN的请求,导致这部分逻辑未执行。
从代码层面看,问题出在 processAllRequests 方法的方向切换逻辑:
if (currentDir.equals(UP) && !hasUpRequests()) {
currentDir = DOWN;
if (allRequestsEmpty()) {
break;
}
} else if (currentDir.equals(DOWN) && !hasDownRequests()) {
currentDir = UP;
if (allRequestsEmpty()) {
break;
}
}
这里的判断条件 !hasUpRequests() 仅检查 “是否还有上行请求”,但未考虑反向请求的存在。当电梯上行到 7 层后,hasUpRequests() 返回false,于是切换为下行,但此时需要重新检查是否有下行请求(如6,DOWN),而代码中缺少这一步的循环触发,导致方向切换后未继续处理。
请求去重逻辑的局限性
requestSet 用于去重,但键的生成方式(如"INTERNAL_" + targetFloor)仅针对 “相同楼层的内部请求” 去重,未考虑时间维度的合理请求(比如同一楼层不同时间的外部请求应被允许)。
getNearestTarget 算法的效率问题
每次获取最近目标时,都将所有请求加入ArrayList再遍历。当请求量较大时,性能会明显下降,可优化为维护 “最近上行目标” 和 “最近下行目标” 两个变量,避免全量遍历。
四·改进建议:对相应题目的编码改进给出自己的见解,做到可持续改进
问题描述
当电梯上行到最高层后,没有正确切换到下行方向处理请求。
例如:输入 6,DOWN,电梯到达 7 层后,没有返回 6 层处理下行请求。
原因:processAllRequests() 中方向切换逻辑不完整,切换方向后没有重新检查请求队列。
改进方案:
重写调度逻辑,采用 “同向优先,顺路服务” 原则:
每次移动前,确定当前方向上的最远目标楼层。
移动到目标楼层,途中处理所有顺路请求。
到达目标后,重新评估方向,处理反方向请求。
问题描述
使用 LinkedList 存储请求,contains() 方法效率低(O (n))。
无法快速找到 “最远” 或 “最近” 的请求。
改进方案:
使用 TreeSet 存储请求,自动排序,支持快速查找。
外部上行请求:TreeSet(升序),方便找最远的上行车层。
外部下行请求:TreeSet(降序),方便找最远的下行车层。
内部请求:TreeSet,方便快速判断是否包含当前楼层。
问题描述
所有逻辑都写在 Elevator 类中,职责不单一。
难以添加新功能(如电梯容量限制、优先级请求)。
改进方案:
采用 单一职责原则,拆分功能:
Elevator 类:管理电梯状态和基本操作。
RequestProcessor 类:处理请求解析和调度逻辑。
InputHandler 类:处理用户输入。
五·总结
从面向对象设计、算法逻辑、代码工程化三个维度收获了大量知识
1. 面向对象设计能力的深化
类的职责拆分:从最初将所有逻辑塞在Elevator类中,到后来拆分出RequestProcessor、InputHandler等类,深刻理解了单一职责原则。例如电梯类只负责状态管理,请求处理器专注于调度算法,输入处理类负责解析用户指令,这种拆分让代码结构更清晰,维护成本大幅降低。
枚举的高效运用:用Direction和ElevatorState枚举替代字符串常量后,不仅避免了拼写错误,还让代码的类型安全性和可读性显著提升。枚举的 “有限状态” 特性非常适合电梯方向、运行状态这类场景,这是面向对象中 “抽象” 思想的绝佳实践。
数据结构的选型:从LinkedList切换到TreeSet(含降序集合)后,请求的排序、查找效率得到质的提升。例如外部上行请求用升序TreeSet,可以快速获取 “当前楼层之上的最远请求”,这让调度算法的实现从 O (n) 优化到 O (log n),体现了数据结构与算法逻辑的强关联性。
2. 算法逻辑的打磨
电梯调度算法的理解:从最初对 “同方向优先” 规则的模糊实现,到后来清晰的 “确定目标→移动并处理顺路请求→切换方向” 流程,掌握了电梯调度的核心逻辑:
先处理当前方向的所有顺路请求,到达最远目标后再切换方向;
初始方向选择需考虑 “最近请求” 的优先级;
每次停靠时要同时处理内部请求和外部同方向请求。
边界情况的处理:在迭代中逐步覆盖了 “无效楼层请求”“同楼层重复请求”“方向切换后的请求残留” 等边界场景。例如当电梯从上行切换为下行时,必须重新检查下行方向的请求队列,否则会出现 “漏处理请求” 的问题,这让我对逻辑完整性有了更深刻的认识。
3. 代码工程化能力的提升
测试驱动的思维:为了验证调度逻辑的正确性,我开始有意识地设计测试用例(如 “先上后下的请求组合”“跨多层的请求”),通过对比预期输出和实际运行结果来排查问题。这种 “先想清楚逻辑,再写代码” 的思路,让调试效率提升了很多。
重构的勇气与方法:从第一版的 “能跑就行” 到后来的 “结构优化”,我学会了如何在不破坏原有功能的前提下,逐步重构代码。例如先替换枚举,再拆分类,最后优化数据结构,每一步都通过测试验证,这种小步迭代的重构策略非常适合复杂逻辑的代码优化。
最终感悟
通过这三次迭代,对 “编程是一个迭代优化的过程” 有了切身体会:
先跑通,再优化:刚开始不必追求完美的设计,先把核心逻辑跑通,再逐步重构和优化。如果一开始就纠结于细节,很容易陷入 “停滞不前” 的困境。
重视业务逻辑的梳理:电梯调度这类问题,核心是理解 “用户请求→电梯响应” 的流程。在写代码前,用流程图或伪代码把逻辑理清楚,能避免 80% 的 bug。
代码的可读性比 “炫技” 重要:过于复杂的设计和技巧,会让后续维护者(包括自己)望而却步。清晰的命名、合理的注释、简洁的逻辑,才是代码的 “生命线”。
测试是质量的保障:没有测试的代码,就像没有质检的产品,功能再花哨也可能存在致命缺陷。通过设计多场景的测试用例,可以大幅提升代码的可靠性。

浙公网安备 33010602011771号