记电梯问题的实践总结

前言:

这三次电梯问题是根据乘客的内外部请求,模拟电梯运行原理针对不同的输入来进行相应输出(包括开门、楼层变化和关门)。首先第一次电梯问题仅仅是设计好电梯运行算法就行,第二次则进行多余输入的删除,第三次则不再进行方向的输入,而是直接通过乘客请求来合并请求列表。在这三次作业中最难的是最基本的算法设计,包括输入的读取,从输入中读取有效信息形成列表,以及对请求列表 的运行等;而根据以上难点,以及第二、三次算法迭代中要实现的功能都离不开正则表达式和ArrayList的使用。本次Blog主要针对本人对电梯运行算法的改进,以及正则表达式,ArrayList的使用展开。

设计与分析:

第一次电梯问题:

 

类图:

 

 

 

 

 

算法分析:

1. 初始化阶段

  1. 程序启动:创建BufferedReader读取用户输入

  2. 创建电梯对象Elevator elevator = new Elevator()

  3. 设置楼层范围

    • 读取第一行输入作为minFloor(最低楼层)

    • 读取第二行输入作为maxFloor(最高楼层)

  4. 设置初始位置currentFloor = 1(电梯初始停在1楼)

2. 请求输入阶段

程序进入循环,等待用户输入请求,直到输入"END":

  • 外部请求格式<楼层,方向>

  • 内部请求格式<楼层> 

每个请求都会调用processRequest()方法处理:

  1. 去除<>

  2. 判断是外部请求(含逗号)还是内部请求

  3. 检查楼层是否有效(isValidFloor)

  4. 将有效请求加入对应队列:

    • 内部请求加入internalRequests

    • 外部请求封装为ExternalRequest对象加入externalRequests

3. 电梯运行阶段(elevator.run())

初始状态

  • 方向初始化为UP(向上)

  • 创建StringBuilder收集输出信息

  • 输出初始状态:Current Floor: 1 Direction: UP

主循环

只要hasRequests()返回true(任一队列不为空),电梯持续运行:

上行过程(moveUp)

  1. 当前楼层+1

  2. 输出当前状态

  3. 检查shouldStop

    • 如果当前楼层匹配内部请求队列首元素,开门关门并移除该请求

    • 如果当前楼层匹配外部请求且方向相同,开门关门并移除该请求

    • 如果应该改变方向(shouldChangeDirection),则处理请求并改变方向

  4. 检查是否需要转向:

    • 到达最高楼层,或

    • 上方无任何请求(!hasUpRequestsAbove() && !hasInternalRequestsAbove())

下行过程(moveDown)

  1. 当前楼层-1

  2. 输出当前状态

  3. 检查shouldStop(逻辑同上行)

  4. 检查是否需要转向:

    • 到达最低楼层,或

    • 下方无任何请求(!hasDownRequestsBelow() && !hasInternalRequestsBelow())

停止条件

当两个请求队列都为空时,hasRequests()返回false,循环结束

4. 输出阶段

将StringBuilder中的运行记录输出到控制台

心得体会:

本次作业是第一次接触电梯问题,算法设计的第一大难关便是有用数据的分类读取,在此我选择用正则表达式,可以设计regx,用正则表达式来判断输入内容,用同时创建ArrayList列表,使用ArrayList中的add函数将筛选过后的信息加入不同列表,从而进行后一步的算法。其次,在本次实验中有一大问题就是当外部请求和内部请求都有相同楼层,但是外部请求的楼层是电梯向上运行时打开,内部请求的楼层是电梯向下运行时打开,而我通过与同学交流学习发现同学卡着的难点是相同的楼层只能打开一次。主要是因为我们使用了列表清楚函数,当列表中的一项被运用时及楼层等于当前楼层,列表会直接删除这1楼层。而我们的函数算法中外部楼层和内部楼层并不是同时判断的,并且内部楼层没有方向,从而导致了我们内部楼层被提前删除,进而无法实现相同楼层不同方向而开两次门的要求。在此的改进时,我们可以不用列表删除函数,而是使用链表的遍历,从而弥补算法缺陷。最后在本次作业中面临的问题是超过了内存限制的问题,在此可以将Scanner用BufferdRead因为如果用Scanner需要创建新的字符串,而BufferdRead可以在原字符串的基础上进行修改,从而减少内存的使用,提高代码效率。

第二次电梯问题:

一、

类图:

二、算法分析

1. 初始化阶段

  1. 程序启动

    • 创建BufferedReader对象用于读取用户输入

    BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
  2. 创建电梯对象

    • 实例化电梯控制系统核心组件

    Elevator elevator = new Elevator(minFloor, maxFloor);
    RequestQueue requestQueue = new RequestQueue();
    ElevatorController controller = new ElevatorController(elevator, requestQueue);
  3. 设置楼层范围

    • 读取用户输入的前两行作为电梯运行范围

    int minFloor = Integer.parseInt(br.readLine());  // 最低楼层
    int maxFloor = Integer.parseInt(br.readLine());  // 最高楼层
  4. 设置初始状态

    • 电梯默认停在1楼,初始方向向上

    elevator.currentFloor = 1;
    elevator.direction = 1;  // 1=UP, -1=DOWN

2. 请求输入阶段

  1. 输入循环

    • 持续读取输入直到遇到"END"命令

    while (!(line = br.readLine().trim()).equalsIgnoreCase("END"))
  2. 请求格式处理

    • 外部请求格式:<楼层,方向> 如<3,UP>

    • 内部请求格式:<楼层> 如<5>

    • 去重检查避免处理相同连续请求

    if (!line.equals(previousRequest))
  3. 请求处理流程

    1. 去除<>符号

    2. 根据是否包含逗号判断请求类型

    3. 验证楼层有效性

      floor >= elevator.minFloor && floor <= elevator.maxFloor
    4. 加入对应队列:

      • 内部请求 → internalRequests

      • 外部请求 → externalRequests

3. 电梯运行阶段

初始状态输出

Current Floor: 1 Direction: UP

主循环控制

while (requestQueue.hasRequests()) {
    if (elevator.direction == 1) {
        moveUp();
    } else {
        moveDown();
    }
}

上行过程(moveUp)

  1. 楼层移动

    elevator.moveUp();  // currentFloor++

    输出新状态:Current Floor: [X] Direction: UP

  2. 停靠判断

    • 检查是否匹配内部请求队列首元素

      if (floor == requestQueue.peekInternalRequest().floor)
    • 检查是否匹配外部请求(方向需一致)

      if (floor == requestQueue.peekExternalRequest().floor && dir == request.direction)
  3. 请求处理

    • 移除已处理的请求

      requestQueue.pollInternalRequest();
      requestQueue.pollExternalRequest();
    • 输出开关门动作

      Open Door # Floor [X]
      Close Door
  4. 方向检查

    • 到达顶层或上方无请求时转向

      if (currentFloor == maxFloor || (!hasUpRequestsAbove() && !hasInternalRequestsAbove()))

下行过程(moveDown)

(逻辑与上行对称)

  1. 楼层移动currentFloor--

  2. 停靠判断:同上升逻辑

  3. 方向检查:到达底层或下方无请求时转向

4. 终止条件

  • 当两个请求队列均为空时结束循环

    public boolean hasRequests() {
        return !internalRequests.isEmpty() || !externalRequests.isEmpty();
    }

5. 输出阶段

  • 将StringBuilder中记录的完整运行过程输出到控制台

     
    System.out.print(output.toString());

    三、心得体会:

    本次作业相较于第一次作业算法上改进不大,但是难点在于它的类更细化了,而且增加了一个功能是当重复输入某些内容是只取第一个输入的内容。对于这一个难点,我们可以通过以下内容实现:

    在 Main 类中定义了静态变量 previousRequest 用于记录上一次处理的请求:

    private static String previousRequest = null;  // 保存上一次处理的请求字符串

    在请求处理循环中,通过字符串比对过滤重复输入:

    while (!(line = br.readLine().trim()).equalsIgnoreCase("END")) {
        if (line.startsWith("<") && line.endsWith(">")) {
            // 只有当前请求与上一次不同时才会处理
            if (!line.equals(previousRequest)) {
                processRequest(line, requestQueue, elevator);
                previousRequest = line;  // 更新记录
            }
        }
    }

    我们在这个函数中有一个优点是大小写不敏感,我们使用 equals() 而非 equalsIgnoreCase(),但实际输入已通过 trim() 标准化。

    第三次电梯问题:

    一、

    类图:

    二、算法分析:

     

    1. 系统初始化

    1. 电梯对象创建

      • 读取用户输入的minFloormaxFloor确定服务范围

      • 初始化电梯状态:

        • currentFloor = 1(默认起始层)

        • direction = 1(初始上行方向)

      • 创建空的请求队列(internalRequestsexternalRequests

    2. 控制器就绪

      • 绑定电梯实例与请求队列

      • 初始化输出记录器


    2. 请求接收阶段

    1. 输入循环

      • 持续读取输入直到"END"指令

      • 每行输入需满足格式:

        • 外部请求:<源楼层,目标楼层>

        • 内部请求:<目标楼层>

    2. 请求处理流程

      • 格式解析

        1. 去除< >符号

        2. 按逗号判断请求类型

      • 有效性验证

        • 检查楼层是否在[minFloor, maxFloor]范围内

      • 队列存储

        • 内部请求:直接加入internalRequests队列

        • 外部请求:封装为Passenger对象加入externalRequests队列


    3. 电梯运行阶段

    主循环逻辑

    plaintext
     
    复制
     
    下载
    while (存在未处理请求):
        if (当前方向 == 上行):
            执行moveUp()
        else:
            执行moveDown()

    上行过程(moveUp)

    1. 移动楼层

      • currentFloor++

      • 输出新状态(当前楼层+方向)

    2. 停靠判断

      • 内部请求:检查当前楼层是否匹配队列首个元素

        • 匹配则移除请求并开关门

      • 外部请求:检查当前楼层是否有同方向乘客

        • 接载乘客后将其目标楼层加入内部队列

        • 特殊情况下触发方向变更(通过shouldChangeDirection()

    3. 方向决策

      • 满足以下任一条件则转为下行:

        • 到达maxFloor

        • 上方无任何待处理请求(通过hasUpRequests()hasInternalRequestsAbove()判断)

    下行过程(moveDown)

    (与上行对称)

    1. 移动楼层currentFloor--

    2. 停靠判断:同方向外部请求+内部请求

    3. 方向决策:到达minFloor或下方无请求时转上行


    4. 终止条件

    • 循环退出:当两个请求队列均为空时,hasRequests()返回false

    • 输出结果:打印完整的运行日志,包含:

      • 所有楼层变更记录

      • 方向切换记录

      • 开关门操作记录

    三、心得体会:

    第三次写电梯问题相较于前两次的算法自然是更完善的。首先前两版中逻辑集中在Elevator类中混合状态管理和请求处理,不太明确。第三版Elevator是仅维护电梯的物理状态及楼层和方向,RequestQueue独立管理请求队列ElevatorController中。处理调度算法这些符合了单一职责原则更易于拓展维护。

    踩坑心得:

    通过这三次电梯问题的设计实践,我得出以下踩坑心得:

    1、对于输入内容的筛选是困扰我的第一个问题,当面对不规则字符的输入我们可以选择运用正则表达式,同时如果我们需要将字符串收集形成一个列表从而进行运算,我们可以用ArrayList,这也必须要求我们掌握Array List中的相关函数应用。

    2、当我们发现自己的算法输出后的结果与要求不同时或与目标不同时,首先我们应该先定位到它是里不同,是根本错误还是仅是一点,然后我们观察这一点所对应的输入是否与其他输入是否有特殊之地,然后我们可以在代码中针对这一不同,然后设计一个变量进行调试。通过查看变量的变化来确定算法到底在哪儿出现了问题,从而对算法进行更新维护。

    3、我们在平时的作业中总会遇到内存超限的情况,此时我们可以用BufferdRead来代替Scanner,因为BufferdRead可以在原有字符串的基础上进行修改,而Scanner必需创建一个新的字符串。

    4、遍历数组在JAVA中很常用,对于某些需要删除数组元素的一定要谨慎,可以用便利来代替删除。

     

    改进建议:

    本人最近在学习正则表达式的应用,发现里面的Pattern、Matcher好用,在Matcher中的group可以提取正则表达式中的某一些元素,进而加入列表,使代码更加清晰,逻辑明确。本人希望可以在接下来的代码练习中。学习正则表达式更多的函数来加以应用,附录中针对大象进冰箱问题中本人使用了较多的正则表达式,可以参考一下。同时本人对算法中的循环运用的并不清晰,熟练。针对电梯问题,本人算法较为复杂冗长,可以针对上述正则表达式的学习,从而思考出更为明了的算法,例如可以用matcher. group来将请求更细化分类,然后设计更为明确针对某一变量仅针对某一变量来行使单一职责。同时本人代码缺少注释等使本人代码的可读性并不高,在以后的代码练习中需要改进。

     

    总结:

    Java的学习不是一蹴而就的的过程,例如就本次作业需要用ArrayList一样,要掌握Java这门语言,需要我们不断地自主学习,我们可以通过自主学习来运用Java中自带的方法,从而简化代码,提高效率。

    同时针对本次作业,主要体现了我之前修改算法的能力不足,并不知道从哪入手修改,首先我们应该先定位到它是里不同,是根本错误还是仅是一点,然后我们观察这一点所对应的输入是否与其他输入是否有特殊之地,然后我们可以在代码中针对这一不同,然后设计一个变量进行调试。通过查看变量的变化来确定算法到底在哪儿出现了问题,从而对算法进行更新维护。

    之后我的第一个任务是学好正则表达式,和ArrayList,真的太使用了,本人已经记了很多方法,但需要更多的运用来记住,会使用。

     

    附录:

    前文提到的可供参考的大象进冰箱问题:

    import java.io.File;
    import java.io.FileNotFoundException;
    import java.util.ArrayList;
    import java.util.Scanner;
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;
    
    public class Main{
        public static void main(String[] args) throws FileNotFoundException{
            Scanner scanner=new Scanner(System.in);
            ArrayList<Elephant> elephants=new ArrayList<>();
            IceBox box=new IceBox();
            File file=new File("文件路径");
            Scanner in=new Scanner(file);
            String regex="(\\d*),(\\d*),(\\d*),((\\d*)\\.(\\d*)\\.(\\d*))";
    
            while(in.hasNextLine()){
                String s=in.nextLine();
                Pattern pattern=Pattern.compile(regex);
                Matcher matcher=pattern.matcher(s);
                if(matcher.matches()){
                    Elephant e=new Elephant();
                    e.name=matcher.group(1);
                    e.weight=Integer.valueOf(matcher.group(2));
                    e.length=Integer.valueOf(matcher.group(3));
                    e.time=matcher.group(4);
                    e.time1=Integer.parseInt(matcher.group(5));
                    e.time2=Integer.parseInt(matcher.group(6));
                    e.time3=Integer.parseInt(matcher.group(7));
                    box.time.add(matcher.group(4));
                    elephants.add(e);
                }
            }
    
            if(elephants.size()<box.count){
                System.out.println("冰箱门打开,大象进入冰箱");
                System.out.println("已进入冰箱的大象的信息是:");
                int c=0;
                for(int i=0;i<elephants.size();i++){
                    System.out.println("第"+(i+1)+"只象:");
                    elephants.get(i).show();
                    c++;
                }
                System.out.println("冰箱门关上\n");
                System.out.println("冰箱的名字:"+box.name);
                System.out.println("冰箱可容纳的大象的数量:"+box.count+"只");
                System.out.println("冰箱现有大象数量:"+c+"只");
                int way=1;
                while(way<5){
                    IceBox.show(way,elephants);
                    way++;
                }
            }
            scanner.close();
        }
    }
    
    
    class Elephant{
        String name;
        int weight;
        int length;
        String time;
        int time1;
        int time2;
        int time3;
    
        public void show(){
            if(time!=null){
                System.out.println("大象的名字:"+name);
                System.out.println("大象的体重:"+weight+"kg");
                System.out.println("大象的身高:"+length+"m");
                System.out.println("大象进入冰箱的时间:"+time);
            }
        }
    }
    
    
    class Sort{
    
        //1.冒泡排序(按名字)
        public static void bubbleSortByName(ArrayList<Elephant> elephants){
            for(int i=0;i<elephants.size()-1;i++){
                for(int j=0;j<elephants.size()-1-i;j++){
                    if(elephants.get(j).name.compareTo(elephants.get(j+1).name)>0){
                        Elephant temp=elephants.get(j);
                        elephants.set(j,elephants.get(j+1));
                        elephants.set(j+1,temp);
                    }
                }
            }
        }
    
        //2.选择排序(按重量)
        public static void selectionSortByWeight(ArrayList<Elephant> elephants){
            for(int i=0;i<elephants.size()-1;i++){
                int minIndex=i;
                for(int j=i+1;j<elephants.size();j++){
                    if(elephants.get(j).weight<elephants.get(minIndex).weight){
                        minIndex=j;
                    }
                }
                if(minIndex!=i){
                    Elephant temp=elephants.get(i);
                    elephants.set(i,elephants.get(minIndex));
                    elephants.set(minIndex,temp);
                }
            }
        }
        //3.插入排序(按高度)
        public static void insertionSortByLength(ArrayList<Elephant> elephants){
            for(int i=1;i<elephants.size();i++){
                Elephant key=elephants.get(i);
                int j=i-1;
                while(j>=0&&elephants.get(j).length>key.length){
                    elephants.set(j+1,elephants.get(j));
                    j--;
                }
                elephants.set(j+1,key);
            }
        }
        //4.归并排序(按大象进入冰箱次序)
        public static void mergeSortByTime(ArrayList<Elephant> elephants){
            if(elephants.size()>1){
                ArrayList<Elephant> left=new ArrayList<>();
                ArrayList<Elephant> right=new ArrayList<>();
                int middle=elephants.size()/2;
                for(int i=0;i<middle;i++){
                    left.add(elephants.get(i));
                }
                for(int i=middle;i<elephants.size();i++){
                    right.add(elephants.get(i));
                }
                mergeSortByTime(left);
                mergeSortByTime(right);
    
                merge(elephants,left,right);
            }
        }
        private static void merge(ArrayList<Elephant> elephants,
                                  ArrayList<Elephant> left,
                                  ArrayList<Elephant> right){
            int i=0,j=0,k=0;
            while(i<left.size()&&j<right.size()){
                if(left.get(i).time1<right.get(j).time1){
                    elephants.set(k++,left.get(i++));
                }
                else if(left.get(i).time1>right.get(j).time1){
                    elephants.set(k++,right.get(j++));
                }
                else if(left.get(i).time2<right.get(j).time2){
                    elephants.set(k++,left.get(i++));
                }
                else if(left.get(i).time2>right.get(j).time2){
                    elephants.set(k++,right.get(j++));
                }
                else if(left.get(i).time3<right.get(j).time3){
                    elephants.set(k++,left.get(i++));
                }
                else{
                    elephants.set(k++,right.get(j++));
                }
            }
            while(i<left.size()){
                elephants.set(k++,left.get(i++));
            }
            while(j<right.size()){
                elephants.set(k++,right.get(j++));
            }
        }
    
    
    }
    
    
    class IceBox{
        String name="冰箱";
        int count=5;
        ArrayList<String> time=new ArrayList<>();
    
        public static void show(int way,ArrayList<Elephant> elephants){
            if(way==1){
                Sort.bubbleSortByName(elephants);
                System.out.println("\n按名字排序(冒泡排序):");
                for(int i=0;i<elephants.size();i++){
                    System.out.println("大象"+(i+1)+"的名字:"+elephants.get(i).name);
                    System.out.println("大象"+(i+1)+"的体重:"+elephants.get(i).weight+"kg");
                    System.out.println("大象"+(i+1)+"的身高:"+elephants.get(i).length+"m");
                    System.out.println("大象"+(i+1)+"进入冰箱的时间:"+elephants.get(i).time);
                }
            }
            else if(way==2){
                Sort.selectionSortByWeight(elephants);
                System.out.println("\n按重量排序(选择排序):");
                for(int i=0;i<elephants.size();i++){
                    System.out.println("大象"+(i+1)+"的名字:"+elephants.get(i).name);
                    System.out.println("大象"+(i+1)+"的体重:"+elephants.get(i).weight+"kg");
                    System.out.println("大象"+(i+1)+"的身高:"+elephants.get(i).length+"m");
                    System.out.println("大象"+(i+1)+"进入冰箱的时间:"+elephants.get(i).time);
                }
            }
            else if(way==3){
                Sort.insertionSortByLength(elephants);
                System.out.println("\n按身高排序(插入排序):");
                for(int i=0;i<elephants.size();i++){
                    System.out.println("大象"+(i+1)+"的名字:"+elephants.get(i).name);
                    System.out.println("大象"+(i+1)+"的体重:"+elephants.get(i).weight+"kg");
                    System.out.println("大象"+(i+1)+"的身高:"+elephants.get(i).length+"m");
                    System.out.println("大象"+(i+1)+"进入冰箱的时间:"+elephants.get(i).time);
                }
            }
            else if(way==4){
                Sort.mergeSortByTime(elephants);
                System.out.println("\n按大象进入冰箱次序排序(归并排序):");
                for(int i=0;i<elephants.size();i++){
                    System.out.println("大象"+(i+1)+"的名字:"+elephants.get(i).name);
                    System.out.println("大象"+(i+1)+"的体重:"+elephants.get(i).weight+"kg");
                    System.out.println("大象"+(i+1)+"的身高:"+elephants.get(i).length+"m");
                    System.out.println("大象"+(i+1)+"进入冰箱的时间"+elephants.get(i).time);
                }
            }
        }
    }

     

posted @ 2025-04-19 22:14  邵星瑶  阅读(77)  评论(0)    收藏  举报