电梯的迭代与分析

一.前言
个人感觉这几次的题目集重在进一步完善java的语法问题(链表)和面向对象的分析。最重要的某过于就是对于需求的解读,本次的题目集是建立在一个电梯题目的基础之上的,但是实际上的需求与现实里面的电梯大不相同,如果像我一样的莽夫可能就是要走很多的弯路的,因此这点还是很关键的,其次就是对于逻辑上的设计和算法上面的优化了,不仅要做出来,而且需要做的好点。当然了,首先是做出来,也就是从0到1的过程,而后的从1到未来的发展也很重要。这就是我的些许微薄感触了。
二.设计与分析
题目分析
(1)原题:
设计一个电梯类,具体包含电梯的最大楼层数、最小楼层数(默认为1层)当前楼层、运行方向、运行状态,以及电梯内部乘客的请求队列和电梯外部楼层乘客的请求队列,其中,电梯外部请求队列需要区分上行和下行。
电梯运行规则如下:电梯默认停留在1层,状态为静止,当有乘客对电梯发起请求时(各楼层电梯外部乘客按下上行或者下行按钮或者电梯内部乘客按下想要到达的楼层数字按钮),电梯开始移动,当电梯向某个方向移动时,优先处理同方向的请求,当同方向的请求均被处理完毕然后再处理相反方向的请求。电梯运行过程中的状态包括停止、移动中、开门、关门等状态。当电梯停止时,如果有新的请求,就根据请求的方向或位置决定移动方向。电梯在运行到某一楼层时,检查当前是否有请求(访问电梯内请求队列和电梯外请求队列),然后据此决定移动方向。每次移动一个楼层,检查是否有需要停靠的请求,如果有,则开门,处理该楼层的请求,然后关门继续移动。
使用键盘模拟输入乘客的请求,此时要注意处理无效请求情况,例如无效楼层请求,比如超过大楼的最高或最低楼层。还需要考虑电梯的空闲状态,当没有请求时,电梯停留在当前楼层。
请编写一个Java程序,设计一个电梯类,包含状态管理、请求队列管理以及调度算法,并使用一些测试用例,模拟不同的请求顺序,观察电梯的行为是否符合预期,比如是否优先处理同方向的请求,是否在移动过程中处理顺路的请求等。为了降低编程难度,不考虑同时有多个乘客请求同时发生的情况,即采用串行处理乘客的请求方式(电梯只按照规则响应请求队列中当前的乘客请求,响应结束后再响应下一个请求),具体运行规则详见输入输出样例。

(2)分析

首先是通过读取存储到链表里面,初始化电梯时,楼层1设为最低层,方向默认 “UP”,并输出初始状态 “Current Floor: 1 Direction: UP”(假设最低层为 1)。进入请求处理循环后,processRequests来判断当前的行情况以及是否需要停下:上行时,持续上升直到无更高楼层的内部请求或同向外部请求(楼层 > 当前且方向 UP),每到一层调用checkRequests,若当前楼层与队列首个请求匹配且方向一致则停梯开关门,并移除该请求;若外部请求与当前运行运方向相反但楼层符合条件(如上行时遇到下方的 DOWN 请求且无更高目标的特殊情况下),则切换方向。下行逻辑类似,处理低于当前楼层的请求。如此往复,直到内外请求队列为空,循环结束。过程中实时打印楼层和方向变化。
三.踩坑心得
1.首先拿到了题目的我看到电梯以及大体的描述便下意识当现实中的具体的电梯,参考输入的格式:
第一行输入最小电梯楼层数。
第二行输入最大电梯楼层数。
从第三行开始每行输入代表一个乘客请求。

电梯内乘客请求格式:<楼层数>
电梯外乘客请求格式:<乘客所在楼层数,乘梯方向>,其中,乘梯方向用UP代表上行,用DOWN代表下行(UP、DOWN必须大写)。
当输入“end”时代表输入结束(end不区分大小写)。
我们可以发现是一次性的读入全部的数据,因此最开始我的想法是保证优先处理同向原则的情况下,我需要对输入的数据进行排序,使得电梯到达相应的楼层可以依次的打开。
而电梯停靠的一般来说是在第一层。
这样子就会导致第一个疑问,电梯一定是向上的,这样子就会处理掉所有的内部请求和所有的外部向上的请求,意思是电梯只需要向上到达最高的楼就可以下来,期间只有一次的方向变换,虽然与实际不符合,但考虑到这个是一次性的读入,初始的印象就是整个题目并不复杂。但是题目所给的唯一一个(刚刚开始的时候)测试样例:
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
Current Floor: 6 Direction: DOWN
Open Door # Floor 6
Close Door
Current Floor: 5 Direction: DOWN
Current Floor: 4 Direction: DOWN
Current Floor: 3 Direction: DOWN
Open Door # Floor 3
Close Door
与初始的想法明显不符合,而后老师相继发了有关题目的详细解释,这个时候才醒悟过来。
这个是一个披着电梯外套的另有逻辑的JAVA题目。即电梯每次只是处理内部请求和外部请求的第一个数据,而后判断方向进行下一步。
因此每次对需求进行分析的时候都不能凭借“感觉”,“我觉得”的想法。而是认真的考虑题目的要求。
开始的我打算是使用数组来解决这个问题的,由于外部部请求含有方向,因此计划使用二维数组,先使用正则表达式来获取<>里面的数据,将其存储到数组里面,再使用.lenght的方式获取数组的长度,每次到达楼层,判断以后就向后进一位,但实际操作还是比较繁琐的。
而后老师又给出了以链表为基础的mian方法,于是我又将原先的数组该为了更加高效好用的链表。
再通过了初始的样例以后,题目只有一个测试点,却还是提交失败,证明了逻辑还是有问题。
而后老师又补充了三个测试的样例:
其中样例一有一步
< 3,DOWN>
<2>
<5,UP>
<1>
结果显示电梯先在2楼停靠,而后3楼停靠以后向下了
而在样例二里面
<5,DOWN>
<8>
<10>
<4,UP>
在8楼停靠以后,去往了10楼。
结果就是内部请求结束,需要看外部请求和下一个内部请求,由此来判断方向,而外部请求自带方向,由当前的外部请求和内部请求来判断方向。彻底搞清楚了逻辑以后,我就开始编码了,由于当前自身的能力有限,这个过程就变得相当的长。其次就是PTA里面实在是不好编,就转战了Idea,虽然刚刚开始不熟悉,但是里面的单步调试确实是很好用,选择一个断点,开始单步调试,当前楼层,方向,内部请求,外部请求等数据实时的变化以及当前程序运行到哪一步都可以看到,就算是逻辑错误进行了无限循环,真的是比只会给出运行超时,一头雾水的PTA好太多了。
第三个样例是有关于特殊的情况
1
10
<4>
<4,UP>
<4,DOWN>
end
结果是在四楼开关门了两次,说明链表.Remove()一次是可以同时将内外的全部进行删除
由此就有了最后的代码

点击查看代码
import java.util.ArrayList;
import java.util.Scanner;

class Elevator {
    private int currentFloor;
    private final int minFloor;
    private final int maxFloor;
    private String direction;
    private ArrayList<Integer> internalRequests;
    private ArrayList<ExternalRequest> externalRequests;

    public static class ExternalRequest {
        int floor;
        String direction;

        public ExternalRequest(int floor, String direction) {
            this.floor = floor;
            this.direction = direction;
        }
    }

    public Elevator(int minFloor, int maxFloor) {
        this.currentFloor = minFloor;
        this.minFloor = minFloor;
        this.maxFloor = maxFloor;
        this.direction = "UP";
        this.internalRequests = new ArrayList<>();
        this.externalRequests = new ArrayList<>();
    }

public int getCurrentFloor() {
        return currentFloor;
    }

    public void setCurrentFloor(int currentFloor) {
        this.currentFloor = currentFloor;
    }

    public String getDirection() {
        return direction;
    }

    public void setDirection(String direction) {
        this.direction = direction;
    }

    public void addExternalRequest(int floor, String direction) {
        externalRequests.add(new ExternalRequest(floor, direction));
    }

    public ArrayList<ExternalRequest> getExternalRequests() {
        return externalRequests;
    }

    public void addInternalRequest(int floor) {
        if (!internalRequests.contains(floor)) {
            internalRequests.add(floor);
        }
    }

    public ArrayList<Integer> getInternalRequests() {
        return internalRequests;
    }

    public void checkRequests() {
        boolean shouldStop = false;
        
        // 检查内部请求
        if (!internalRequests.isEmpty() && internalRequests.get(0) == currentFloor) {
            shouldStop = true;
            internalRequests.remove(0);
        }

        // 检查外部请求
        if (!externalRequests.isEmpty() && 
            externalRequests.get(0).floor == currentFloor && 
            externalRequests.get(0).direction.equals(direction)) {
            shouldStop = true;
            externalRequests.remove(0);
        }

        // 复杂外部请求处理逻辑
        if (!externalRequests.isEmpty() && 
            externalRequests.get(0).floor == currentFloor && 
            ((externalRequests.get(0).floor > internalRequests.get(0) && 
              externalRequests.get(0).direction.equals("DOWN")) || 
             (externalRequests.get(0).floor < internalRequests.get(0) && 
              externalRequests.get(0).direction.equals("UP")))) {
            shouldStop = true;
            externalRequests.remove(0);
            if(direction.equals("UP")){
            direction = "DOWN";
            }else{
                direction = "UP";
            }
        }

        if (shouldStop) {
            System.out.println("Open Door # Floor " + currentFloor);
            System.out.println("Close Door");
        }
        if (internalRequests.isEmpty() && externalRequests.isEmpty()) {
            return;
        }
    }

public void processRequests() {
    while(direction.equals("UP")) {
        // 仅处理UP方向的请求(内部楼层>当前,或外部楼层>当前且方向UP)
        while (!internalRequests.isEmpty() && internalRequests.get(0) > currentFloor ||
               !externalRequests.isEmpty() && externalRequests.get(0).floor > currentFloor 
              && direction.equals("UP")) {
            currentFloor++;
            printCurrentState();
        }
        direction = "DOWN";
    }
    while(direction.equals("DOWN")){
        // 仅处理DOWN方向的请求(内部楼层<当前,或外部楼层<当前且方向DOWN)
        while (!internalRequests.isEmpty() && internalRequests.get(0) < currentFloor ||
               !externalRequests.isEmpty() && externalRequests.get(0).floor < currentFloor
              && direction.equals("DOWN")) {
            currentFloor--;
            printCurrentState();
        }
        direction = "UP";
    }
}

    private void printCurrentState() {  //输出函数
        System.out.println("Current Floor: " + currentFloor + " Direction: " + direction);
        checkRequests();
    }
}

public class Main {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        ArrayList<String> list = new ArrayList<>();

        int minFloor,maxFloor; //电梯最低楼层、最高楼层
        String request = ""; //提取出的乘客请求字符串

        while (input.hasNext()) {
            String data = input.next();
            if (data.equalsIgnoreCase("End")) break;
            list.add(data);
        }

        minFloor = Integer.parseInt(list.get(0));//第一行,电梯最低楼层
        maxFloor = Integer.parseInt(list.get(1));//第二行,电梯最高楼层

        Elevator elevator = new Elevator(minFloor, maxFloor);//创建电梯对象

        for (int i = 2; i < list.size(); i++) {//从第三行开始为乘客请求
            request = list.get(i);
            if (request.contains(",")) {//外部请求
                String[] parts = request.replaceAll("[<>]", "").split(",");
                int floor = Integer.parseInt(parts[0].trim());
                String direction = parts[1].trim().toUpperCase();
                elevator.addExternalRequest(floor, direction);//外部请求入队,格式为<楼层数,方向>
            } else {
                if (!request.matches("<\\d+>")) {//内部请求
                    System.out.println("Wrong Format");
                }
                int floor = Integer.parseInt(request.replaceAll("[<>]", ""));
                elevator.addInternalRequest(floor);//内部请求入队,格式为<楼层数>
            }
        }

        System.out.println("Current Floor: 1 Direction: UP");
        while(!elevator.getInternalRequests().isEmpty() || !elevator.getExternalRequests().isEmpty()){
            elevator.processRequests(); //一次性处理所有的内、外部乘客请求
        }
        input.close();
    }
}

但是就是这个最后的代码还是有点问题,我到现在还是没有解决。就是我在Idea上面都是样例全对的代码放到PTA上面却显示运行超时,我也问了一些人包括老师,得到的可能是Idea没有运行时间的限制,但是我也用了特别简单的例子,但是PTA限制时间400ms如果没有循环的基本是不可能运行超时的。哎,还得练(╥╯﹏╰╥)ง
这个是运行截图:

四.改进建议
1.楼层的输入检查,第二次题目集里面有要求
2.逻辑的优化,当前每次到一个楼层都需要进行所有的判断,比较难受。
3.可读性,英语一直不是很好,之前甚至一直是在用拼音来标准的,驼峰命名法等,注释也没有写太多。
五.总结
这次的三个题目集都没有写出来,说明了我自身能力的欠缺与努力程度的不足。java的基础语法依旧没有很好的掌握,每次的拼写以及判断方向时对链表状态的判断的欠缺。专业工具Idea的使用还有待加强。不过也教会了我很多吧,首先就是抗击打能力,三次的题目集都没有写出来我没有就是很摆烂的,后面也很认真的在补了,也逐渐的把AI用来学习,而不仅仅是copy,其次呢就是面向对象的需求分析,这次题目集我花了大量的时间,走了非常多的弯路来确定了题目的意思,如果提前去看了输入的几个样例和老师的思维导图的话,可能就不会这样子了把。至于下一次的题目集我是准备好了吗?可能还没有,但已经在路上了。

posted @ 2025-04-20 19:50  昨夜忘做梦  阅读(14)  评论(0)    收藏  举报