PTA题目集567电梯迭代

一、前言

  这三次电梯题目系统地考察了Java编程中的多项核心技能,主要包括类的设计、集合框架使用(如数组和链表)、数据操作(排序、查找、删除)等关键技术。我认为这三次作业最核心的挑战在于算法逻辑的设计与实现。具体来看,题目集5作为基础篇,主要考察基本数据处理能力和简单调度逻辑的实现。题目集6在此基础上增加了面向对象设计的复杂度,需要处理电梯与乘客的交互关系,特别是要解决不合理请求过滤和重复请求去重的问题。到了题目集7,题目要求更加全面,不仅逻辑复杂度显著提升,还需要考虑更接近实际场景的各种特殊情况。总体而言,这三次题目采用了循序渐进的迭代设计方式,从最基础的电梯调度功能开始,逐步增加功能模块和业务复杂度,最终形成一个相对完整的电梯调度系统。这种由浅入深的设计思路虽然对Java初学者来说颇具挑战性,但确实能有效提升我们的编程能力和系统设计思维。特别值得一提的是重量级电梯题目,它完美体现了"迭代开发"的思想。每次题目都在前一次基础上增加新功能,要求我们不断重构和优化代码结构。这种设计让我深刻理解了软件工程中"增量开发"的重要性。

二、设计与分析

题目集5:

1.核心结构分析

  • ​分层处理​​:将电梯系统分为主控模块(Main)和电梯核心模块(Elevator)
  • ​请求分类​​:区分内部请求()和外部请求(<n,UP/DOWN>)
  • ​状态管理​​:通过direction(IDLE/UP/DOWN)和state(Stopped)跟踪电梯状态

2. 请求处理流程

  1. ​输入解析​​:

    • 使用Scanner读取最小/最大楼层
    • 循环读取请求直到"end"指令
    • 通过正则表达式区分内外请求
  2. ​请求分类存储​​:

    • 内部请求存入innerElevator
    • 外部请求存入outerElevator
  3. ​调度执行​​:

    • 调用performActions()开始处理请求队列

3. 核心算法逻辑

  • ​多条件调度策略​​:

     
    if (!innerRequests.isEmpty() && !outerRequests.isEmpty()) {
        // 处理同时存在的内外请求
    } else if (outerRequests.isEmpty()) {
        // 仅处理内部请求
    } else if (innerRequests.isEmpty()) {
        // 仅处理外部请求
    }
  • ​方向决策机制​​:

    • 空闲状态时根据第一个请求决定方向
    • 运行中优先处理同方向请求
    • 通过sameDirection()判断请求方向一致性
  • ​移动控制​​:

     
    elevatorMove(currentFloor, targetFloor, direction);
    requests.remove(0); // 处理完移除请求

4. 关键设计特点

  1. ​状态驱动​​:

    • 通过direction状态决定处理逻辑
    • 三种主要状态:IDLE/UP/DOWN
  2. ​请求优先级​​:

    • 同方向请求优先处理
    • 内部请求和同方向外部请求比较距离
  3. ​移动模拟​​:

    • elevatorMove()实现楼层遍历打印
    • 包含开门关门动作模拟
  4. ​异常处理​​:

    • 通过extractFloorNumber()安全提取楼层号
    • 请求格式校验使用正则表达式

5.时间复杂度分析

  1. ​请求处理主循环​​:

    • performActions() 的 while 循环:O(N),N 为总请求数
    • 内部每次调用 handleBothRequests() 等处理方法:O(1) 基本操作
  2. ​请求匹配和移除​​:

    • ArrayList 的 remove(0) 操作:O(N)(需要移动元素)
    • 最坏情况下(交替处理内外请求):O(N²)
  3. ​移动模拟​​:

    • elevatorMove() 的楼层遍历:O(M),M 为楼层跨度

6.空间复杂度分析

  1. ​存储结构​​:

    • 两个 ArrayList 存储请求:O(N)
    • 固定数量的状态变量:O(1)
  2. ​临时变量​​:

    • 方法调用栈:O(K),K 为递归深度(当前实现无递归)
    • 楼层遍历时的循环变量:O(1)

​总体空间复杂度​​:O(N)

题目集6:

1. 核心结构分析

​分层架构​​:

  • Passenger:数据模型层,封装乘客的起始和目标楼层
  • RequestQueue:数据管理层,处理请求的存储和检索
  • Elevator:设备控制层,负责电梯移动和状态维护
  • Controller:调度逻辑层,实现核心调度算法

​请求分类​​:

  • 内部请求:<n>格式,直接存入TreeSet自动排序
  • 外部请求:<n,d>格式,存入LinkedList保持顺序

​状态管理​​:

  • 使用Direction枚举(UP/DOWN/IDLE)精确控制方向
  • 通过current字段实时跟踪电梯位置

2. 请求处理流程

​输入解析​​:

// 读取楼层范围
int min = sc.nextInt();  
int max = sc.nextInt();

// 持续读取请求
while (true) {
    String line = sc.nextLine().trim();
    if (line.equalsIgnoreCase("end")) break;
    q.add(line); // 请求入队
}

​请求存储​​:

  • 正则表达式<(\\d+)(?:,(\\d+))?>解析请求
  • 内部请求存入TreeSet实现自动排序
  • 外部请求存入LinkedList保持到达顺序

​调度执行​​:

new Controller(elevator, queue).process(); // 启动调度引擎

3. 核心算法逻辑

​调度策略​​:

while (true) {
    handleCurrent(); // 处理当前楼层
    targets = getTargets(); // 获取同向目标
    if (targets.isEmpty()) {
        targets = getOppositeTargets(); // 检查反向目标
        elevator.reverseDir(); // 必要时转向
    }
    moveTo(target); // 执行移动
}

​方向决策​​:

  • 优先处理当前方向的请求
  • 同方向无请求时自动反转
  • 通过NavigableSet.tailSet/headSet高效定位目标

​移动控制​​:

void moveTo(int target) {
    Direction newDir = target > current ? UP : DOWN;
    if (newDir != dir) updateDirection(newDir);
    
    while (current != target) {
        current += (dir == UP) ? 1 : -1;
        printStatus(); // 实时输出状态
    }
}

4. 关键设计特点

​数据结构优化​​:

  • TreeSet保证O(logN)的查询效率
  • LinkedList实现O(1)的请求删除
  • NavigableSet提供高效的范围查询

​状态管理​​:

  • 枚举确保方向状态的安全性
  • 每次移动前检查方向一致性
  • 通过reverse()方法简化方向切换

​异常处理​​:

private boolean valid(int f) {
    return f >= minFloor && f <= maxFloor; // 楼层范围校验
}

5. 时间复杂度分析

​请求处理​​:

  • 请求添加:O(logN)(TreeSet自动排序)
  • 请求查询:O(1)~O(logN)(取决于数据结构)
  • 请求移除:O(1)(LinkedList)/O(logN)(TreeSet)

​移动过程​​:

  • 楼层遍历:O(M)(M为移动跨度)
  • 方向切换:O(1)常量时间

​总体复杂度​​:

  • 最优情况:O(NlogN + M)
  • 最差情况:O(N²)(极端请求分布)

6. 空间复杂度分析

​存储结构​​:

  • TreeSet存储内部请求:O(N)
  • LinkedList存储外部请求:O(M)
  • 电梯状态变量:O(1)

​临时消耗​​:

  • 方法调用栈:O(1)(无递归)
  • 移动过程变量:O(1)

​总体空间​​:O(N + M)(与请求量线性相关)

题目集7:

1. 核心结构分析

​分层架构​​:

  • Passenger:乘客数据模型,记录源楼层、目标楼层和电梯内状态
  • Elevator:电梯实体,管理内部请求队列和当前乘客
  • RequestQueue:外部请求队列,存储待处理的乘客请求
  • ElevatorController:调度中枢,协调电梯和请求队列的交互

​状态管理​​:

  • 电梯方向状态:UP/DOWN字符串表示
  • 乘客状态:isInsideElevator标志位
  • 楼层范围验证:通过minFloor/maxFloor约束

2. 请求处理流程

​输入处理​​:

// 读取楼层范围
int minFloor = scanner.nextInt();
int maxFloor = scanner.nextInt();

// 持续解析请求
Pattern.compile("<(\\d+)(?:,(\\d+))?>");  // 正则匹配两种请求格式

​请求分发​​:

  • 内部请求(单数字):elevator.addInternalRequest()
  • 外部请求(双数字):requestQueue.addPassenger()

​调度循环​​:

while (!requestQueue.isEmpty() || elevator.hasInternalRequests()) {
    // 1. 判断是否开门
    // 2. 处理上下客
    // 3. 决定移动方向
    // 4. 执行移动
}

3. 核心算法逻辑

​开门条件判断​​:

boolean shouldOpenDoor() {
    // 条件1:当前楼层有内部请求
    // 条件2:当前楼层有同方向外部请求
    // 条件3:外部请求目标方向与电梯当前方向匹配
}

​方向决策​​:

void determineDirection() {
    if (有内部请求) {
        根据最近内部请求调整方向
    } else {
        扫描外部请求:
        - 仅上方有请求:向上
        - 仅下方有请求:向下
        - 上下都有请求:保持原方向
    }
}

​乘客流转​​:

// 上客处理:
if (乘客方向与电梯方向一致) {
    elevator.addPassenger(p);
    requestQueue.removePassenger(p);
}

// 下客处理:
passengers.removeIf(p -> p到达目标楼层);

4. 关键设计特点

​数据结构选择​​:

  • 内部请求:Queue<Integer>(FIFO处理)
  • 外部请求:ArrayList<Passenger>(随机访问)
  • 当前乘客:ArrayList<Passenger>(便于批量移除)

​状态同步机制​​:

  • 通过isInsideElevator标志追踪乘客位置
  • 方向变更时自动更新内部请求处理顺序
  • 每次移动后立即更新楼层显示

​异常防护​​:

// 楼层有效性检查
if (floor >= minFloor && floor <= maxFloor) {...}

// 请求去重
!internalRequests.contains(floor)

5. 时间复杂度分析

​关键操作​​:

  • 请求添加:

    • 内部请求:O(1)(Queue追加)
    • 外部请求:O(1)(ArrayList追加)
  • 请求处理:

    • 乘客上下车:O(N)(ArrayList遍历)
    • 方向决策:O(M)(外部请求扫描)
  • 移动过程:

    • 每层移动:O(1)基本操作

​总体复杂度​​:

  • 最优情况:O(N + M)(线性处理)
  • 最差情况:O(N²)(大量同楼层请求)

6. 空间复杂度分析

​存储消耗​​:

  • Queue<Integer>:O(K)(内部请求数)
  • ArrayList<Passenger> x2:O(N+M)(乘客+外部请求)
  • 电梯状态:O(1)固定

​临时消耗​​:

  • 方法调用栈:O(1)(无递归)
  • 临时集合:O(N)(处理时的乘客拷贝)

​总体空间​​:O(N + M + K)(与请求量成正比)

三、踩坑心得

题目集5的惨痛教训

​问题1:超时但测试样例通过​

我的第一次提交所有测试样例都通过了,但却因为超时被判0分。😭

问题2:没有正确看待题目难度
第一次电梯题目开始时存在一些问题,当时对题目掉以轻心,到最后没有留出足够的时间来解决问题,最后导致可用时间过少,最后也没有修改成功。

题目集6的调试历程

 1.​​初始阶段问题​

  • 方向逻辑混乱:电梯在空载时频繁改变方向
  • 测试用例:<1,3>+<5,2>组合请求时出现无效往返
  • 解决方法:重构determineDirection(),增加外部请求扫描优先​

  2.边界条件

  • 极端案例:<MAX,MIN>+<MIN,MAX>同时请求
  • 问题:电梯在端点楼层方向判断错误
  • 补丁:在move()方法增加楼层越界保护

  3.状态同步​

  • 偶发BUG:开门后未及时移除内部请求
  • 现象:电梯在目标楼层反复开关门
  • 修复:在openDoor()后立即执行removeNextInternalRequest()
 

可惜最后仍然无法通过测试点,通过修改也无法解决问题,最后超过了截止时间😭


题目集7的突破

​关键发现:回溯前两次作业​

在题目集7卡壳时,我意识到问题可能源自前两次作业的基础设计。于是我从前两次作业入手,经过修改前两次作业,成功通过了第一次作业,第三次作业时的大部分也顺利通过。

但是还是卡在了其中一个样例上,客户从上到下的过程存在问题,最终没有通过测试点。

四、改进建议

1. 先把基础功能做扎实 

我发现自己经常被复杂的调度算法绕晕,其实应该先保证:

  • 电梯能正确响应​​最基本的上下请求​​(比如单独处理"<2>"或"<1,4>")
  • ​移动逻辑​​要完全正确(UP时+1,DOWN时-1)
  • ​开关门​​时机要精确(比如到达请求楼层必须停)

2. 学会用测试用例debug 

踩过的坑:

  • 以为代码没问题,但PTA就是不给过
  • 自己想的测试用例太简单(比如只测连续上楼)

现在我会:

// 专门测试边界情况的例子
"<1>"         // 最小楼层请求
"<MAX>"       // 最大楼层请求 
"<1,MAX>"     // 跨全楼请求
"<2,1>"       // 反方向请求
"<1>" + "<1>" // 重复请求
 

3. 代码结构要整洁 

之前把全部逻辑堆在main()里,现在学会了:

  • 用​​多个类​​分工(比如把乘客、电梯、控制器分开)
  • 方法不超过20行(长的就拆成小方法)
  • 重要逻辑加注释,比如:
 
// 判断是否开门的三要素:
// 1. 有内部请求 2. 有外部请求 3. 方向匹配

4. 数据结构要选对 

血泪教训:用ArrayList频繁删除元素超级慢!

优化方案:

  • 内部请求改用PriorityQueue自动排序
  • 乘客列表用LinkedList快速增删
  • 楼层检查用HashSet快速查询

5. 学会"作弊"技巧 

从同学那里学来的实用技巧:

  • 先用正则表达式<(\d+),?(\d+)?>统一处理输入
  • 方向判断用字符串比数字方便(比如直接比较"UP"/"DOWN")
  • 复杂的if-else换成状态模式(虽然我还没完全学会)

6. 时间管理建议 ⏰

  • 不要等到截止前才做(电梯题至少要留3天)
  • 每天实现一个小功能(比如今天搞定方向判断,明天处理请求队列)
  • 遇到卡壳就休息会儿,回来再看经常能发现明显错误

虽然现在的代码还有很多不足,但每次改进都能学到新东西。相信下次会做得更好!🚀

五、总结

1. 学习收获

这三次电梯题目集让我从“面向过程”逐渐转向“面向对象”编程,收获真的很大!

​(1)面向对象思维​

  • 学会了用​​类​​来封装数据和逻辑(比如ElevatorPassengerRequestQueue
  • 理解了​​单一职责原则​​(一个类只做一件事,比如Elevator只管移动,Controller只管调度)
  • 尝试了​​分层设计​​(数据层、逻辑层、控制层分离)

​(2)数据结构与算法​

  • 发现ArrayList删除元素很慢,改用LinkedListPriorityQueue优化
  • 学会了用​​正则表达式​​(PatternMatcher)解析输入
  • 理解了​​时间复杂度​​的重要性(比如O(N²)的嵌套循环会导致超时)

​(3)调试与测试​

  • 学会了用System.out.println()打印关键变量,跟踪程序执行流程
  • 学会了构造​​边界测试用例​​(比如最小/最大楼层、重复请求、反向请求)
  • 发现​​PTA的隐藏测试点​​往往比样例更严格,不能只满足于样例通过

​(4)代码规范与可读性​

  • 学会了写注释(尤其是复杂逻辑,比如方向决策)
  • 学会了拆分长方法(比如把determineDirection()拆成几个小方法)
  • 发现​​命名规范​​很重要(比如currentFloorcf清晰得多)

​2. 哪些地方需要进一步学习?​

虽然进步很大,但还有很多不足:

​(1)设计模式​

  • 目前代码还是“硬编码”逻辑,想学​​状态模式​​(State Pattern)来优化电梯方向管理
  • 听说​​观察者模式​​(Observer Pattern)适合处理事件驱动,想试试

​(2)算法优化​

  • 现在的调度算法还是“先到先服务”,想学更智能的​​LOOK算法​​或​​SCAN算法​
  • 想研究怎么用​​优先队列​​(PriorityQueue)优化请求处理顺序

​(3)调试技巧​

  • 目前主要靠println,想学会用​​IDEA的Debugger​​(断点、单步执行)
  • 想学​​单元测试​​(JUnit),避免手动测试太麻烦

​(4)代码复用​

  • 现在每次作业都重写很多代码,想学会​​抽象通用逻辑​​(比如Request基类)
  • 想研究​​接口​​(Interface)和​​继承​​怎么用更合理

​3. 改进建议

​(1)课程 & 教学​
优点​​:老师讲解很清晰,尤其是类设计和正则表达式部分

 ​​建议​​:

能不能在讲完理论后,​​给个完整的小Demo​​(比如一个简化版电梯代码)?

希望多讲​​调试技巧​​(比如怎么用Debugger找BUG)

​(2)作业 & 实验​
 ​​优点​​:PTA自动评测很方便,能快速知道对错
 ​​建议​​:

希望​​提前发布作业要求​​(比如周末发布,下周一交,时间有点紧)

能不能​​开放部分隐藏测试点​​?有时候样例过了但隐藏点没过,不知道怎么改

posted @ 2025-04-20 23:19  谷恒早苗  阅读(17)  评论(0)    收藏  举报