NCHU_电梯阶段总结
前言
题目一:
知识点:
类的设计与实现,包括定义电梯类并封装其属性(最大楼层数、最小楼层数、当前楼层、运行方向、运行状态、请求队列等)和方法。
队列的使用,管理电梯内部和外部的请求队列。
电梯调度算法的实现,遵循特定的运行规则(优先处理同方向请求等)。
输入输出的处理,从键盘读取乘客请求并按格式输出电梯运行过程。
条件判断,处理无效请求(如超过楼层范围)。
题量: 需设计一个电梯类并实现其完整的功能逻辑,包括请求处理和状态管理等,同时编写测试用例模拟不同请求顺序观察电梯行为,题量适中。
难度:中等难度。主要难点在于设计电梯调度算法并正确处理请求队列,同时要考虑各种边界情况(如无效请求)和电梯的不同状态转换。
题目二:
知识点:
单一职责原则(SRP)的应用,将电梯调度程序拆分为多个类(电梯类、乘客请求类、队列类、控制类),每个类负责单一的职责。
类之间的交互和协作,通过不同类的方法调用实现电梯的完整功能。
数据处理,处理乘客请求楼层数有误(超出范围)和不合理请求(连续相同请求)的情况。
与题目一相同的电梯调度算法、输入输出处理和请求队列管理等知识点。
题量: 相比题目一,增加了类的设计和类之间交互的实现,题量有所增加。需设计多个类并确保它们之间的正确协作,同时处理特殊请求情况。
难度:中等到较高难度。除了要实现电梯的基本功能外,还需遵循单一职责原则进行类的设计和拆分,处理特殊请求情况也增加了一定的复杂性。
题目三:
知识点:
进一步应用单一职责原则,引入乘客类,取消乘客请求类,并重新设计类之间的关系和交互。
乘客请求输入格式的变化及处理,将外部请求修改为 <请求源楼层,请求目的楼层>,并在处理请求后将目的楼层加入内部队列。
与前两题相同的电梯调度算法、请求队列管理、输入输出处理和电梯状态管理等知识点。
对类的职责划分和数据流动的理解,确保各个类之间的协作正确处理乘客请求和电梯运行。
题量: 在题目二的基础上,修改了请求输入格式并引入新的逻辑(处理外部请求后将目的楼层加入内部队列),同时要重新设计类的职责和交互,题量较大。
难度:较高难度。不仅要处理电梯的复杂调度逻辑,还要适应请求输入格式的变化和新的逻辑要求,同时合理设计类之间的关系以遵循单一职责原则,增加了整体的复杂性和难度。
*设计与分析
题目一:
提交结果:运行超时。
源码类图:

解释如下:
Elevator 类:
属性:
minFloor:int 类型,表示电梯的最小楼层数。
maxFloor:int 类型,表示电梯的最大楼层数。
currentFloor:int 类型,表示电梯当前所在的楼层。
direction:String 类型,表示电梯的运行方向(如 "UP"、"DOWN")。
state:String 类型,表示电梯的运行状态(如 "STOPPED"、"MOVING")。
internalRequests:List类型,存储电梯内部乘客的请求楼层列表。
upExternalRequests:List类型,存储电梯外部向上的请求列表。
downExternalRequests:List类型,存储电梯外部向下的请求列表。
方法:
Elevator(int minFloor, int maxFloor):构造方法,用于初始化电梯的最小楼层数和最大楼层数等属性。
addInternalRequest(int floor):向电梯内部请求列表添加一个楼层请求。
addExternalRequest(int floor, String direction):向电梯外部请求列表(根据方向)添加一个请求。
isValidFloor(int floor):判断给定楼层是否在有效范围内。
determineDirection():根据请求情况确定电梯的运行方向。
findNextFloor():找到下一个要到达的楼层。
findNextUpFloor():找到下一个要到达的向上的楼层。
findNextDownFloor():找到下一个要到达的向下的楼层。
hasRequests():判断是否还有未处理的请求。
run():运行电梯,处理请求并输出电梯运行过程。
shouldStop(int floor, String elevatorDirection):判断电梯在当前楼层是否应该停靠。
hasExternalRequest(int floor):判断在指定楼层是否有外部请求。
stopAtFloor(int floor):处理电梯在指定楼层停靠时的操作(开门、移除请求、关门)。
ExternalRequest 类:
属性:
floor:int 类型,表示请求的楼层。
direction:String 类型,表示请求的方向(如 "UP"、"DOWN")。
方法:
ExternalRequest(int floor, String direction):构造方法,用于初始化请求的楼层和方向。
Main 类:
方法:
main(String[] args):程序的入口方法,用于读取用户输入,创建 Elevator 对象,添加请求并运行电梯。
类之间的关系:
ExternalRequest类是Elevator类的内部静态类,它们之间存在包含关系。
Main类通过创建Elevator对象来使用Elevator类的功能.
心得:逻辑还是太过混乱,应该有更清晰的思路再写代码,否则运行超时出现死循环。
第二题:
运行结果:答案错误。
源码类图:

解释如下:
Elevator 类
功能概述: 该类用于表示电梯,封装了电梯的基本属性和操作,如当前方向、当前楼层、最大楼层、最小楼层等,并提供了获取和设置这些属性的方法,以及判断楼层是否有效的方法。
属性:
direction:表示电梯的运行方向,初始值为 "UP"。
currentFloor:表示电梯当前所在的楼层,初始值为 1。
maxFloor:表示电梯能够到达的最大楼层。
minFloor:表示电梯能够到达的最小楼层。
方法:
构造函数Elevator(int minFloor, int maxFloor):用于初始化电梯的最小楼层、最大楼层,以及设置默认的方向和当前楼层。
getDirection():获取电梯当前的运行方向。
setDirection(String direction):设置电梯的运行方向。
getCurrentFloor():获取电梯当前所在的楼层。
setCurrentFloor(int currentFloor):设置电梯当前所在的楼层。
getMaxFloor():获取电梯的最大楼层。
getMinFloor():获取电梯的最小楼层。
isValidFloor(int floor):判断给定的楼层是否在电梯可运行的有效楼层范围内。
Controller 类
功能概述: 该类是电梯的控制类,负责根据请求队列中的请求来决定电梯的运行方向,控制电梯的移动,判断电梯是否应该在某楼层停下,以及处理电梯的开关门操作。
属性:
elevator:一个Elevator对象,用于操作电梯的属性和状态。
queue:一个RequestQueue对象,用于获取电梯的内部和外部请求队列。
方法:
构造函数Controller(Elevator elevator, RequestQueue requestQueue):初始化控制器,传入电梯对象和请求队列对象。
determineDirection():根据内部请求队列和外部请求队列的情况,决定电梯的运行方向。
move():控制电梯的移动,根据当前方向和请求队列,不断移动电梯并在需要的楼层停下。
shouldStop(int floor):判断电梯是否应该在指定的楼层停下,考虑了内部请求和外部请求的情况。
getClosest(Integer a, Integer b):获取两个楼层中距离电梯当前楼层较近的楼层,用于在决策方向时进行选择。
openDoors():处理电梯的开关门操作,输出开关门的信息。
ExternalRequest 类
功能概述: 该类用于表示电梯的外部请求,封装了外部请求的楼层和方向信息,并提供了获取和设置这些信息的方法。
属性:
floor:表示外部请求的楼层。
direction:表示外部请求的方向,如 "UP" 或 "DOWN"。
方法:
构造函数ExternalRequest(Integer floor, String direction):初始化外部请求的楼层和方向。
getFloor():获取外部请求的楼层。
setFloor(Integer floor):设置外部请求的楼层。
getDirection():获取外部请求的方向。
setDirection(String direction):设置外部请求的方向。
RequestQueue 类
功能概述: 该类用于管理电梯的请求队列,包括内部请求队列(电梯内部乘客的请求)和外部请求队列(电梯外部乘客的请求),提供了添加请求和获取请求队列的方法。
属性:
internalRequests:一个LinkedList类型的内部请求队列。
externalRequests:一个LinkedList类型的外部请求队列。
方法:
构造函数RequestQueue():初始化请求队列,创建内部和外部请求队列的实例。
getInternalRequests():获取内部请求队列。
setInternalRequests(LinkedList internalRequests):设置内部请求队列。
getExternalRequests():获取外部请求队列。
setExternalRequests(LinkedList externalRequests):设置外部请求队列。
addInternalRequest(int floor):向内部请求队列中添加一个内部请求。
addExternalRequest(ExternalRequest externalRequest):向外部请求队列中添加一个外部请求。
Main 类
功能概述: 该类是程序的入口点,负责从控制台读取用户输入的电梯信息和请求信息,创建电梯、请求队列和控制器对象,并调用控制器的move()方法来启动电梯的运行。
方法:
main(String[] args):程序的主方法,实现了读取输入、创建对象和启动电梯运行的逻辑。
心得:处理特殊输入时只需要在前几个电梯程序基础上进行小部分变化即可,split()和replaceAll()的方法很实用。并且学习到了可以split()[index]的方法,加深我对split的理解。不过电梯的核心处理逻辑我还是没用改进,依旧有问题,并没有理解到核心逻辑(按顺序分队列以及合适的入队与出队表达)。
第三题:
运行结果:

源码类图:

解释如下:
RequestQueue 类
功能概述:该类用于管理电梯的请求队列,包括内部请求队列、外部请求队列以及特殊楼层队列。
成员变量:
private LinkedList inRequest:存储电梯内部乘客的请求,即乘客想要到达的目标楼层。
private LinkedList outRequest:存储电梯外部乘客的请求,即乘客所在的起始楼层和想要到达的目标楼层。
private LinkedList specialFloor:存储特殊楼层,用于处理一些特殊情况,例如某些楼层需要额外停靠。
构造函数:
RequestQueue(LinkedList inRequest, LinkedList outRequest):初始化内部请求队列、外部请求队列,并将特殊楼层队列初始化为空。
方法:
LinkedList getInRequest():返回内部请求队列。
void setInRequest(LinkedList inRequest):如果内部请求队列不为空,则移除队列中的第一个元素。
LinkedList getOutRequest():返回外部请求队列。
LinkedList getSpecialFloor():返回特殊楼层队列。
Elevator 类
功能概述:该类表示电梯,负责管理电梯的当前楼层、最小楼层、最大楼层以及运行方向。
成员变量:
private int currentFloor:电梯当前所在的楼层。
private int minFloor:电梯运行的最小楼层。
private int maxFloor:电梯运行的最大楼层。
private String direction:电梯的运行方向,取值为 "UP" 或 "DOWN"。
构造函数:
Elevator(int minFloor, int maxFloor):初始化电梯的当前楼层为 1,设置最小楼层和最大楼层,并将电梯的初始运行方向设置为 "UP"。
方法:
int getCurrentFloor():返回电梯当前所在的楼层。
void setCurrentFloor(int currentFloor):设置电梯当前所在的楼层。
int getMinFloor():返回电梯运行的最小楼层。
void setMinFloor(int minFloor):设置电梯运行的最小楼层。
int getMaxFloor():返回电梯运行的最大楼层。
void setMaxFloor(int maxFloor):设置电梯运行的最大楼层。
String getDirection():返回电梯的运行方向。
void setDirection(String direction):设置电梯的运行方向。
boolean isValidFloor(int floor):判断给定的楼层是否在电梯运行的最小楼层和最大楼层之间。
Passenger 类
功能概述:该类表示乘客,存储乘客的起始楼层和目标楼层。
成员变量:
private Integer sourceFloor:乘客所在的起始楼层。
private Integer destinationFloor:乘客想要到达的目标楼层。
构造函数:
Passenger():默认构造函数。
Passenger(Integer sourceFloor, Integer destinationFloor):初始化乘客的起始楼层和目标楼层。
Passenger(Integer destinationFloor):仅初始化乘客的目标楼层,适用于内部请求。
方法:
Integer getSourceFloor():返回乘客的起始楼层。
void setSourceFloor(Integer sourceFloor):设置乘客的起始楼层。
Integer getDestinationFloor():返回乘客的目标楼层。
void setDestinationFloor(Integer destinationFloor):设置乘客的目标楼层。
Controller 类
功能概述:该类是电梯的控制器,负责管理电梯的运行逻辑,包括确定下一个停靠楼层、处理停靠事件、确定电梯运行方向等。
成员变量:
private Elevator elevator:电梯对象,用于获取和设置电梯的状态。
private RequestQueue requestQueue:请求队列对象,用于管理电梯的请求。
构造函数:
Controller(Elevator elevator, RequestQueue requestQueue):初始化电梯对象和请求队列对象。
方法:
Elevator getElevator():返回电梯对象。
RequestQueue getRequestQueue():返回请求队列对象。
int findNextFloor():根据电梯的当前楼层和运行方向,确定下一个停靠的楼层。考虑了内部请求队列和外部请求队列的情况。
void handleStop(int floor):处理电梯在指定楼层的停靠事件。如果有内部请求或外部请求与该楼层匹配,则移除相应的请求,并更新内部请求队列。如果该楼层是特殊楼层,则标记为开门。如果开门条件满足,则调用 openDoor() 方法,并重新确定电梯的运行方向。
void determineDirection():根据当前楼层和下一个停靠楼层,确定电梯的运行方向。
void move():电梯的主运行方法,使用循环不断移动电梯,直到所有请求都处理完毕。在每次移动前,打印当前楼层和运行方向。如果当前楼层是特殊楼层,则开门并移除该特殊楼层。如果需要在当前楼层停靠,则处理停靠事件,并重新确定电梯的运行方向。否则,直接更新电梯的运行方向和当前楼层。
boolean shouldStop(int floor):判断电梯是否需要在指定楼层停靠。如果内部请求队列、外部请求队列或特殊楼层队列中有与该楼层匹配的请求,则返回 true,否则返回 false。
void openDoor():打印开门和关门的信息。
Main 类
功能概述:该类是程序的入口点,负责读取用户输入,初始化电梯、请求队列和控制器,并启动电梯的运行。
方法:
public static void main(String[] args):程序的主方法,读取用户输入的最小楼层、最大楼层以及乘客请求信息。将输入信息解析为内部请求队列和外部请求队列,并创建电梯对象、请求队列对象和控制器对象。最后调用控制器的 move() 方法启动电梯的运行。
心得:这一次我多加了findNextDoor方法和handleStop方法,改变了determineDirection方法。我的move方法里面只有一个循环了。我的逻辑也终于理正(还有点小瑕疵),最终通过三个测试点。
踩坑心得:
第一题:测试用例输入会死循环导致运行超时,我得到最大的启示就是千万不要过多的重复代码和循环的嵌套!//添加死循环截图(Idea)
第二题:我原先在move()当中有大量的循环嵌套(分为队列都不为空,内部为空,外部为空)并且循环的条件和逻辑处理不当导致运行超时,见附图,后来增加了
getClosest方法,有效减少了move方法里面的循环数量,不过由于踩坑了没用正确理解电梯逻辑的问题导致答案错误
第三题:第一个测试用来第一次到五楼就是内外请求都为五,并且电梯下来后经过五楼还开门,我就掉进坑里了,我的处理是将这个楼层(同时是内部请求队列头和外部队列头)视为特殊楼层并且加入到specialFloor队列,然后内外这个请求同时删去,当电梯再次经过特殊楼层时会开关门然后删去该队列请求,见源码 if (!requestQueue.getSpecialFloor().isEmpty() && requestQueue.getSpecialFloor().get(0) == currF) {
openDoor();
requestQueue.getSpecialFloor().remove((Integer)currF);
}。但是我后来发现这个处理是错误的(虽然测试用例可以通过),应当看哪个请求先输入,比如测试用例一就是删去外部请求的<5,4>保留内部的<5>。
改进建议
代码结构优化模块化设计:可以进一步将一些功能拆分成更小的方法,例如将输入解析、请求队列管理等功能封装成独立的类或方法,提高代码的可维护性和可测试性。比如,创建一个 InputParser 类专门负责解析用户输入,将输入字符串转换为 Passenger 对象并添加到相应的请求队列中。异常处理:在读取用户输入和处理请求时,添加更多的异常处理逻辑。例如,当用户输入的楼层超出有效范围或者输入格式错误时,能够给出明确的错误提示,避免程序崩溃。在 Main 类中,解析输入时可以添加对输入格式和楼层范围的检查,若不符合要求则抛出异常并进行处理。
性能优化请求队列排序:当前的请求队列是 LinkedList 类型,在查找下一个停靠楼层时,需要遍历队列,时间复杂度较高。可以考虑使用更高效的数据结构,如 PriorityQueue 对请求进行排序,根据楼层和方向进行优先级排序,这样可以将查找下一个停靠楼层的时间复杂度降低。减少不必要的计算:在 findNextFloor 方法中,有很多条件判断和嵌套循环,会增加计算量。可以对逻辑进行优化,提前过滤掉一些不必要的判断,减少计算次数。
3.代码可读性和注释:在关键的代码段和方法中添加详细的注释,解释代码的功能和逻辑,方便其他开发者理解和维护代码。例如,在 findNextFloor 方法中,对每个条件判断的含义进行注释。使用更具描述性的变量名,避免使用过于简单或模糊的变量名。例如,将 inReq 和 outReq 改为 internalRequests 和 externalRequests,提高代码的可读性。
4.测试和验证:测试各种边界条件,如最小楼层、最大楼层、空请求队列等情况,确保程序在各种情况下都能正常运行。
总结
1.我学到的:ArrayList以及LinkedLIst这两个List的应用;入队和出队的理解;LOOK算法的基本应用;对于字符串处理函数(如substring,split,trim,contains,equals,equalsIgnoreCase,replaceAll等)还有正则表达式的应用有更深的理解和掌握;最后就是面向对象编程的思路的进一步理解。
2.需要进一步学习及研究的:关于队列的进一步研究,如TreeMap,因为这个TreeMap可以放键值,键值可以是两个不同的类,很方便,可以更好的应用在电梯的外部请求队列中(虽然LinkedList也可以通过自定义类的实现);关于电梯运行逻辑的掌握可以还不够,因为如果双电梯这样更为复杂的情况我可能就会力不从心了。
3.对教师、课程、作业、实验、课上及课下组织方式等方面的改进建议及意见:
(1)对教师:建议多对于难度大的pta作业进行讲解,当然,是在我们写完后。可以的话多分享实际开发经验,因为我们其实挺多人不知道做项目一般做什么以及怎么做。
(2)对课程:可以给出教学大纲吗,比如哪一阶段该学什么还有期末的小学期又是什么方便我们去预习来适应。
(3)对作业:无。
(4)对实验:这个实验提交系统能不能至少像pta上一样可以tab键补全,最重要的是关键字等能不能换个颜色标注以作区别。
(5)对课上课下组织方式:无。

浙公网安备 33010602011771号