第一次blog作业

电梯系统

前言

第一次系统迭代作业结束了,这三次题目集的题量并不算太大,但对于我这种初学者(菜鸟)来说还是很有压迫感的,以下是我总结的遇到的知识点:

  • 类与对象
    • 类的定义:定义了Main、Elevator、ExternalRequest等类,每个类都有其独特的属性和方法。
    • 对象的创建与使用:在Main类的main方法里创建了Elevator类的对象,并且调用其方法来处理电梯请求。
    • 内部类:ExternalRequest类作为Elevator类的内部类,封装了外部请求。
  • 枚举类型
    • 定义了Direction和ElevatorStatus枚举类型,用于表示电梯的运行方向和状态,增强了代码的可读性与可维护性。
  • 集合框架
    • 队列(Queue):采用LinkedList实现Queue接口,用于存储内部和外部请求,保证请求按照先进先出(FIFO)的顺序处理。
    • 列表(List):使用ArrayList存储输入数据。
    • 集合操作:运用集合的add、remove、isEmpty、peek等方法对请求进行管理和操作。
  • 正则表达式
    • 正则表达式的使用:代码里使用正则表达式<\d+,\s(UP|DOWN)>和<\d+>对输入的请求字符串进行格式验证,保证输入的格式符合要求。\d表示匹配任意数字,+表示匹配前面的元素一次或多次,()用于分组,|表示或的关系,\s表示匹配零个或多个空白字符。

设计与分析

题目集05

  • 题目
点击展开题目

设计一个电梯类,具体包含电梯的最大楼层数、最小楼层数(默认为1层)当前楼层、运行方向、运行状态,以及电梯内部乘客的请求队列和电梯外部楼层乘客的请求队列,其中,电梯外部请求队列需要区分上行和下行。
电梯运行规则如下:电梯默认停留在1层,状态为静止,当有乘客对电梯发起请求时(各楼层电梯外部乘客按下上行或者下行按钮或者电梯内部乘客按下想要到达的楼层数字按钮),电梯开始移动,当电梯向某个方向移动时,优先处理同方向的请求,当同方向的请求均被处理完毕然后再处理相反方向的请求。电梯运行过程中的状态包括停止、移动中、开门、关门等状态。当电梯停止时,如果有新的请求,就根据请求的方向或位置决定移动方向。电梯在运行到某一楼层时,检查当前是否有请求(访问电梯内请求队列和电梯外请求队列),然后据此决定移动方向。每次移动一个楼层,检查是否有需要停靠的请求,如果有,则开门,处理该楼层的请求,然后关门继续移动。
使用键盘模拟输入乘客的请求,此时要注意处理无效请求情况,例如无效楼层请求,比如超过大楼的最高或最低楼层。还需要考虑电梯的空闲状态,当没有请求时,电梯停留在当前楼层。
请编写一个Java程序,设计一个电梯类,包含状态管理、请求队列管理以及调度算法,并使用一些测试用例,模拟不同的请求顺序,观察电梯的行为是否符合预期,比如是否优先处理同方向的请求,是否在移动过程中处理顺路的请求等。为了降低编程难度,不考虑同时有多个乘客请求同时发生的情况,即采用串行处理乘客的请求方式(电梯只按照规则响应请求队列中当前的乘客请求,响应结束后再响应下一个请求),具体运行规则详见输入输出样例。

输入格式:
第一行输入最小电梯楼层数。
第二行输入最大电梯楼层数。
从第三行开始每行输入代表一个乘客请求。
电梯内乘客请求格式:<楼层数>
电梯外乘客请求格式:<乘客所在楼层数,乘梯方向>,其中,乘梯方向用UP代表上行,用DOWN代表下行(UP、DOWN必须大写)。
当输入“end”时代表输入结束(end不区分大小写)。

输出格式:
模拟电梯的运行过程,输出方式如下:
运行到某一楼层(不需要停留开门),输出一行文本:
Current Floor: 楼层数 Direction: 方向
运行到某一楼层(需要停留开门)输出两行文本:
Open Door # Floor 楼层数
Close Door

  • 源码
点击展开代码
import java.util.Scanner;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

public class Main {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        String data;
        int floor = 0;
        Direction direction = null;
        int minFloor, maxFloor;
        String request = "";
        ArrayList<String> list = new ArrayList<>();
        data = input.next();
        while (!data.equalsIgnoreCase("End")) {
            list.add(data);
            data = input.next();
        }

        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(",")) {
                if (!request.matches("<\\d+,\\s*(UP|DOWN)>")) {
                    System.out.println("Wrong Format");
                }

                String[] parts = request.replaceAll("[<>]", "").split(",");
                floor = Integer.parseInt(parts[0].trim());
                direction = Direction.valueOf(parts[1].trim().toUpperCase());
                elevator.addExternalRequest(floor, direction);
            } else {
                if (!request.matches("<\\d+>")) {
                    System.out.println("Wrong Format");
                }
                floor = Integer.parseInt(request.replaceAll("[<>]", ""));
                elevator.addInternalRequest(floor);
            }
        }

        elevator.processRequests();

        input.close();
    }
}

enum Direction {
    UP,
    DOWN,
    IDLE
}

enum ElevatorStatus {
    MOVING,
    STOPPED
}

class Elevator {
    private int minFloor;
    private int maxFloor;
    private Queue<Integer> internalRequests;
    private Queue<ExternalRequest> externalQueue = new LinkedList<>();
    private Direction currentDirection = Direction.IDLE;
    private ElevatorStatus currentStatus = ElevatorStatus.STOPPED;
    private int currentFloor = 1;

    class ExternalRequest {
        private int floor;
        private Direction direction;

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

        public int getFloor() {
            return floor;
        }

        public Direction getDirection() {
            return direction;
        }
    }

    public Elevator(int minFloor, int maxFloor) {
        this.minFloor = minFloor;
        this.maxFloor = maxFloor;
        this.internalRequests = new LinkedList<>();
        this.externalQueue = new LinkedList<>();
    }

    public void addExternalRequest(int floor, Direction direction) {
        if (!checkFloor(floor)) {
            return;
        } else if (direction == null) {
            return;
        }
        externalQueue.add(new ExternalRequest(floor, direction));
    }

    public void addInternalRequest(int floor) {
        if (!checkFloor(floor)) {
            return;
        } else {
            internalRequests.add(floor);
        }
    }

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

    public Queue<ExternalRequest> getExternalRequests() {
        return externalQueue;
    }

    //初始目标
    private void determineInitialDirection() {
        int internalFirstFloor = internalRequests.isEmpty() ? Integer.MAX_VALUE : internalRequests.peek();
        int externalFirstFloor = externalQueue.isEmpty() ? Integer.MAX_VALUE : externalQueue.peek().getFloor();

        if (internalFirstFloor == Integer.MAX_VALUE && externalFirstFloor == Integer.MAX_VALUE) {
            currentDirection = Direction.IDLE;
            return;
        }

        int closestFloor;
        if (Math.abs(internalFirstFloor - currentFloor) < Math.abs(externalFirstFloor - currentFloor)) {
            closestFloor = internalFirstFloor;
        } else {
            closestFloor = externalFirstFloor;
        }

        currentDirection = closestFloor > currentFloor ? Direction.UP : Direction.DOWN;
    }

    //移动
    private void move() {
    int targetFloor = determineNextTarget();
    if (currentFloor == targetFloor) {
        System.out.println("Open Door # Floor " + currentFloor);
        System.out.println("Close Door");
        internalRequests.removeIf(floor -> floor == currentFloor);
    externalQueue.removeIf(req -> 
        req.getFloor() == currentFloor && 
        req.getDirection() == currentDirection);
        return;
    }

    // 移动过程
    while (currentFloor != targetFloor) {
        if (currentDirection == Direction.UP) {
            currentFloor++;
        } else {
            currentFloor--;
        }
        System.out.println("Current Floor: " + currentFloor + " Direction: " + currentDirection);
    }

}

    //下一目标
    private int determineNextTarget() {
    int closestInDirection = findClosestInCurrentDirection();
    if (closestInDirection != -1) {
        return closestInDirection;
    }

    // 切换方向后再次查找
    currentDirection = (currentDirection == Direction.UP) ? Direction.DOWN : Direction.UP;
    closestInDirection = findClosestInCurrentDirection();
    return closestInDirection != -1 ? closestInDirection : currentFloor;
}

private int findClosestInCurrentDirection() {
    int closest = -1;
    int minDistance = Integer.MAX_VALUE;

    // 合并所有有效请求
    List<Integer> targets = new ArrayList<>();
    targets.addAll(internalRequests);
    externalQueue.stream()
        .filter(req -> req.getDirection() == currentDirection)
        .forEach(req -> targets.add(req.getFloor()));

    // 查找最近目标
    for (int target : targets) {
        if ((currentDirection == Direction.UP && target > currentFloor) ||
            (currentDirection == Direction.DOWN && target < currentFloor)) {
            int distance = Math.abs(target - currentFloor);
            if (distance < minDistance) {
                minDistance = distance;
                closest = target;
            }
        }
    }

    return closest;
}

    //方向
    private void determineDirection() {
        boolean hasUpRequest = false;
        boolean hasDownRequest = false;

        if (!internalRequests.isEmpty()) {
            int internalFloor = internalRequests.peek();
            if (internalFloor > currentFloor) {
                hasUpRequest = true;
            } else if (internalFloor < currentFloor) {
                hasDownRequest = true;
            }
        }

        if (!externalQueue.isEmpty()) {
            ExternalRequest externalRequest = externalQueue.peek();
            if (externalRequest.getFloor() > currentFloor && externalRequest.getDirection() == Direction.UP) {
                hasUpRequest = true;
            } else if (externalRequest.getFloor() < currentFloor && externalRequest.getDirection() == Direction.DOWN) {
                hasDownRequest = true;
            }
        }

        if (currentDirection == Direction.UP && !hasUpRequest && hasDownRequest) {
            currentDirection = Direction.DOWN;
        } else if (currentDirection == Direction.DOWN && !hasDownRequest && hasUpRequest) {
            currentDirection = Direction.UP;
        }
    }

    public void processRequests() {
        determineInitialDirection();
        while (!internalRequests.isEmpty() || !externalQueue.isEmpty()) {
            move();
            determineDirection();
        }
        System.out.println("Close Door");
    }

    //检查
    private boolean checkFloor(int floor) {
        return floor >= minFloor && floor <= maxFloor;
    }
}

代码分析

类过于庞大和复杂

解释

  • 整体调度逻辑
    • 请求处理:代码把电梯请求划分成内部请求(乘客在电梯内按下楼层按钮)和外部请求(乘客在楼层外按下上下行按钮)。这些请求会被分别存于 internalRequests 和 externalQueue 队列之中
    • 初始方向确定:determineInitialDirection 方法会依据当前楼层与最近请求楼层的相对位置来确定电梯的初始运行方向
    • 移动逻辑:move 方法让电梯朝着目标楼层移动,到达目标楼层后会开门、处理请求,然后关门
    • 目标确定:determineNextTarget 方法会找出当前方向上最近的目标楼层,若当前方向没有目标楼层,就会切换方向再次查找
    • 方向调整:determineDirection 方法依据当前请求情况来调整电梯的运行方向

心得

第一次处理电梯系统还是很粗糙,先入为主的认为了到达楼层应将列表内符合楼层全部移除,只将先入先出原则用在了方向判断和改变上,导致连测试点都没有通过,直到提交结束后才在同学的提醒下幡然醒悟

题目集06

  • 题目
点击展开题目

对之前电梯调度程序进行迭代性设计,目的为解决电梯类职责过多的问题,类设计要求遵循单一职责原则(SRP),要求必须包含但不限于设计电梯类、乘客请求类、队列类以及控制类
电梯运行规则与前阶段单类设计相同,但要处理如下情况:
乘客请求楼层数有误,具体为高于最高楼层数或低于最低楼层数,处理方法:程序自动忽略此类输入,继续执行
乘客请求不合理,具体为输入时出现连续的相同请求,例如<3><3><3>或者<5,DOWN><5,DOWN>,处理方法:程序自动忽略相同的多余输入,继续执行,例如<3><3><3>过滤为<3>
注意:本次作业类设计必须符合如上要求(包含但不限于乘客请求类、电梯类、请求队列类及控制类,其中控制类专门负责电梯调度过程),凡是不符合类设计要求此题不得分,另外,PTA得分代码界定为第一次提交的最高分代码(因此千万不要把第一次电梯程序提交到本次题目中测试)。

输入格式:
第一行输入最小电梯楼层数。
第二行输入最大电梯楼层数。
从第三行开始每行输入代表一个乘客请求。
电梯内乘客请求格式:<楼层数>
电梯外乘客请求格式:<乘客所在楼层数,乘梯方向>,其中,乘梯方向用UP代表上行,用DOWN代表下行(UP、DOWN必须大写)。
当输入“end”时代表输入结束(end不区分大小写)。

输出格式:
模拟电梯的运行过程,输出方式如下:
运行到某一楼层(不需要停留开门),输出一行文本:
Current Floor: 楼层数 Direction: 方向
运行到某一楼层(需要停留开门)输出两行文本:
Open Door # Floor 楼层数
Close Door

  • 类图

  • 源码

点击展开代码
import java.util.Scanner;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

enum Direction {
    UP,
    DOWN,
    IDLE
}

enum State {
    MOVING,
    STOPPED
}

class Passenger {
    private Integer sourceFloor = null;
    private Integer destinationFloor = null;

    public Passenger() {
    }

    public Passenger(Integer sourceFloor, Integer destinationFloor) {
        this.sourceFloor = sourceFloor;
        this.destinationFloor = destinationFloor;
    }

    public Passenger(Integer destinationFloor) {
        this.destinationFloor = destinationFloor;
    }

    public Integer getSourceFloor() {
        return sourceFloor;
    }

    public void setSourceFloor(Integer sourceFloor) {
        this.sourceFloor = sourceFloor;
    }

    public Integer getDestinationFloor() {
        return destinationFloor;
    }

    public void setDestinationFloor(Integer destinationFloor) {
        this.destinationFloor = destinationFloor;
    }

    public Direction getDirection() {
        if (sourceFloor > destinationFloor) {
            return Direction.DOWN;
        } else {
            return Direction.UP;
        }
    }
}

class Elevator {
    private int currentFloor;
    private Direction direction;
    private State state;
    private int maxFloor;
    private int minFloor;

    public Elevator(int minFloor, int maxFloor) {
        this.minFloor = minFloor;
        this.maxFloor = maxFloor;
        this.currentFloor = minFloor;
        this.direction = Direction.IDLE;
        this.state = State.STOPPED;
    }

    public int getCurrentFloor() {
        return currentFloor;
    }

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

    public Direction getDirection() {
        return direction;
    }

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

    public State getState() {
        return state;
    }

    public void setState(State state) {
        this.state = state;
    }

    public int getMaxFloor() {
        return maxFloor;
    }

    public int getMinFloor() {
        return minFloor;
    }

    public boolean isValidFloor(int floor) {
        return floor >= minFloor && floor <= maxFloor;
    }
}

class RequestQueue {
    private LinkedList<Integer> internalRequests = new LinkedList<>();
    private LinkedList<Passenger> externalRequests = new LinkedList<>();

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

    public LinkedList<Passenger> getExternalRequests() {
        return externalRequests;
    }

    public void addInternalRequest(Integer floor) {
        internalRequests.add(floor);
    }

    public void addExternalRequest(Passenger passenger) {
        externalRequests.add(passenger);
    }
}

class Controller {
    private Elevator elevator;
    private RequestQueue queue;

    public Controller(Elevator elevator, RequestQueue queue) {
        this.elevator = elevator;
        this.queue = queue;
    }

    public void processRequests() {
        System.out.println("Current Floor: " + elevator.getCurrentFloor() + " Direction: " + elevator.getDirection());
        while (!queue.getInternalRequests().isEmpty() || !queue.getExternalRequests().isEmpty()) {
            move();
            determineDirection();
        }
    }

    private void move() {
        Integer targetFloor = getNextFloor();
        if (targetFloor == null) {
            return;
        }

        if (elevator.getCurrentFloor() == targetFloor) {
            openDoors();
            removeRequests(elevator.getCurrentFloor());
            return;
        }

        elevator.setDirection(elevator.getCurrentFloor() < targetFloor ? Direction.UP : Direction.DOWN);

        while (elevator.getCurrentFloor() != targetFloor) {
            int nextFloor = elevator.getCurrentFloor() + (elevator.getDirection() == Direction.UP ? 1 : -1);
            elevator.setCurrentFloor(nextFloor);
            System.out.println("Current Floor: " + elevator.getCurrentFloor() + " Direction: " + elevator.getDirection());

            if (shouldStop(elevator.getCurrentFloor())) {
                openDoors();
                removeRequests(elevator.getCurrentFloor());
                targetFloor = getNextFloor();
                if (targetFloor == null || elevator.getCurrentFloor() == targetFloor) {
                    break;
                }
                elevator.setDirection(elevator.getCurrentFloor() < targetFloor ? Direction.UP : Direction.DOWN);
            }
        }

        if (elevator.getCurrentFloor() == targetFloor) {
            openDoors();
            removeRequests(elevator.getCurrentFloor());
        }
    }

    private Integer getNextFloor() {
        Integer internal = queue.getInternalRequests().peek();
        Passenger externalPassenger = queue.getExternalRequests().peek();
        Integer external = externalPassenger != null ? externalPassenger.getSourceFloor() : null;

        if (internal == null && external == null) {
            return null;
        }

        Direction currentDir = elevator.getDirection();
        int currentFloor = elevator.getCurrentFloor();

        if (currentDir == Direction.IDLE) {
            return getClosest(internal, external);
        }

        Integer candidate = null;
        if (currentDir == Direction.UP) {
            if (internal != null && internal > currentFloor) {
                candidate = internal;
            }
            if (external != null && external > currentFloor && externalPassenger.getDirection() == currentDir) {
                candidate = getClosest(candidate, external);
            }
            if (candidate == null) {
                candidate = getClosest(internal, external);
                if (candidate != null) {
                    elevator.setDirection(candidate > currentFloor ? Direction.UP : Direction.DOWN);
                }
            }
        } else {
            if (internal != null && internal < currentFloor) {
                candidate = internal;
            }
            if (external != null && external < currentFloor && externalPassenger.getDirection() == currentDir) {
                candidate = getClosest(candidate, external);
            }
            if (candidate == null) {
                candidate = getClosest(internal, external);
                if (candidate != null) {
                    elevator.setDirection(candidate > currentFloor ? Direction.UP : Direction.DOWN);
                }
            }
        }

        return candidate != null ? candidate : null;
    }

    private Integer getClosest(Integer a, Integer b) {
        if (a == null) return b;
        if (b == null) return a;
        int current = elevator.getCurrentFloor();
        return (Math.abs(a - current) < Math.abs(b - current)) ? a : b;
    }

    private boolean shouldStop(int floor) {
        if (!queue.getInternalRequests().isEmpty() && queue.getInternalRequests().peek() == floor) {
            return true;
        }

        Passenger passenger = queue.getExternalRequests().peek();
        if (passenger != null && passenger.getSourceFloor() == floor && passenger.getDirection() == elevator.getDirection()) {
            return true;
        }

        return false;
    }

    private void openDoors() {
        System.out.println("Open Door # Floor " + elevator.getCurrentFloor());
        System.out.println("Close Door");
    }

    private void removeRequests(int currentFloor) {
        if (!queue.getInternalRequests().isEmpty() && queue.getInternalRequests().peek() == currentFloor) {
            queue.getInternalRequests().poll();
        }

        Passenger passenger = queue.getExternalRequests().peek();
        if (passenger != null && passenger.getSourceFloor() == currentFloor && passenger.getDirection() == elevator.getDirection()) {
            queue.getExternalRequests().poll();
            queue.getInternalRequests().add(passenger.getDestinationFloor());
        }
    }

    private void determineDirection() {
        boolean hasUp = false;
        boolean hasDown = false;

        Integer internal = queue.getInternalRequests().peek();
        if (internal != null) {
            if (internal > elevator.getCurrentFloor()) {
                hasUp = true;
            } else if (internal < elevator.getCurrentFloor()) {
                hasDown = true;
            }
        }

        Passenger external = queue.getExternalRequests().peek();
        if (external != null) {
            int floor = external.getSourceFloor();
            Direction dir = external.getDirection();
            if (floor > elevator.getCurrentFloor() && dir == Direction.UP) {
                hasUp = true;
            } else if (floor < elevator.getCurrentFloor() && dir == Direction.DOWN) {
                hasDown = true;
            }
        }

        if (elevator.getDirection() == Direction.UP) {
            if (!hasUp && hasDown) {
                elevator.setDirection(Direction.DOWN);
            }
        } else if (elevator.getDirection() == Direction.DOWN) {
            if (!hasDown && hasUp) {
                elevator.setDirection(Direction.UP);
            }
        } else {
            if (hasUp || hasDown) {
                elevator.setDirection(hasUp ? Direction.UP : Direction.DOWN);
            }
        }
    }
}

public class Main {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        String data;
        int floor = 0;
        Direction direction = null;
        int minFloor, maxFloor;
        String request = "";
        java.util.ArrayList<String> list = new java.util.ArrayList<>();
        data = input.next();
        while (!data.equalsIgnoreCase("End")) {
            list.add(data);
            data = input.next();
        }

        minFloor = Integer.parseInt(list.get(0));
        maxFloor = Integer.parseInt(list.get(1));

        Elevator elevator = new Elevator(minFloor, maxFloor);
        RequestQueue requestQueue = new RequestQueue();
        Controller controller = new Controller(elevator, requestQueue);

        for (int i = 2; i < list.size(); i++) {
            request = list.get(i);
            if (request.contains(",")) {
                if (!request.matches("<\\d+,\\s*(UP|DOWN)>")) {
                    System.out.println("Wrong Format");
                }

                String[] parts = request.replaceAll("[<>]", "").split(",");
                floor = Integer.parseInt(parts[0].trim());
                direction = Direction.valueOf(parts[1].trim().toUpperCase());
                requestQueue.addExternalRequest(new ExternalRequest(floor, direction));
            } else {
                if (!request.matches("<\\d+>")) {
                    System.out.println("Wrong Format");
                }
                floor = Integer.parseInt(request.replaceAll("[<>]", ""));
                requestQueue.addInternalRequest(floor);
            }
        }

        controller.processRequests();

        input.close();
    }
}

代码分析

方法和类过于庞大和复杂,而代码注释过少

解释

  • 整体架构
    • Passenger:用来表示乘客,包含起始楼层、目标楼层以及行进方向等信息
    • Elevator:代表电梯,涵盖当前楼层、运行方向、状态(移动或停止)、最大楼层和最小楼层等属性
    • RequestQueue:管理内部请求(电梯内乘客按下的目标楼层按钮)和外部请求(电梯外乘客发出的呼叫请求)
    • Controller:核心调度类,负责处理请求、移动电梯、确定运行方向等操作
  • 调度逻辑
    • 调度逻辑较题目集05除全面改成FIFO外没有太大变化,故不做赘述

心得

第一次迭代,立刻将所有对队列的处理都改成符合先入先出的原则了,顺利通过了测试样例一,但是又在补充的测试样例上翻了车,没有考虑到内外请求队列队首距离相等且均需要变换方向时候的目标楼层选择,还需要多多思考,老师给出的6+n测试样例很有帮助

题目集07

  • 题目
点击展开题目

对之前电梯调度程序再次进行迭代性设计,加入乘客类(Passenger),取消乘客请求类,类设计要求遵循单一职责原则(SRP),要求必须包含但不限于设计电梯类、乘客类、队列类以及控制类
电梯运行规则与前阶段相同,但有如下变动情况:
乘客请求输入变动情况:外部请求由之前的<请求楼层数,请求方向>修改为<请求源楼层,请求目的楼层>
对于外部请求,当电梯处理该请求之后(该请求出队),要将<请求源楼层,请求目的楼层>中的请求目的楼层加入到请求内部队列(加到队尾)
注意:本次作业类设计必须符合如上要求(包含但不限于设计电梯类、乘客类、队列类以及控制类),凡是不符合类设计要求此题不得分,另外,PTA得分代码界定为第一次提交的最高分代码(因此千万不要把第一次及第二次电梯程序提交到本次题目中测试)。

输入格式:
第一行输入最小电梯楼层数。
第二行输入最大电梯楼层数。
从第三行开始每行输入代表一个乘客请求。

电梯内乘客请求格式:<楼层数>
电梯外乘客请求格式:<请求源楼层,请求目的楼层>,其中,请求源楼层表示乘客发起请求所在的楼层,请求目的楼层表示乘客想要到达的楼层。
当输入“end”时代表输入结束(end不区分大小写)。
输出格式:
模拟电梯的运行过程,输出方式如下:
运行到某一楼层(不需要停留开门),输出一行文本:
Current Floor: 楼层数 Direction: 方向
运行到某一楼层(需要停留开门)输出两行文本:
Open Door # Floor 楼层数
Close Door

  • 类图

  • 源码

点击查看完整 Java 代码
import java.util.Scanner;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

//方向
enum Direction {
    UP,
    DOWN,
    IDLE
}

//状态
enum State {
    MOVING,
    STOPPED
}

//乘客类
class Passenger {
    private Integer sourceFloor = null;
    private Integer destinationFloor = null;

    public Passenger() {
    }

    public Passenger(Integer sourceFloor, Integer destinationFloor) {
        this.sourceFloor = sourceFloor;
        this.destinationFloor = destinationFloor;
    }

    public Passenger(Integer destinationFloor) {
        this.destinationFloor = destinationFloor;
    }

    public Integer getSourceFloor() {
        return sourceFloor;
    }

    public void setSourceFloor(Integer sourceFloor) {
        this.sourceFloor = sourceFloor;
    }

    public Integer getDestinationFloor() {
        return destinationFloor;
    }

    public void setDestinationFloor(Integer destinationFloor) {
        this.destinationFloor = destinationFloor;
    }

    public Direction getDirection() {
        if (sourceFloor > destinationFloor) {
            return Direction.DOWN;
        } else {
            return Direction.UP;
        }
    }
}

//电梯类
class Elevator {
    private int currentFloor;
    private Direction direction;
    private State state;
    private int maxFloor;
    private int minFloor;

    public Elevator(int minFloor, int maxFloor) {
        this.minFloor = minFloor;
        this.maxFloor = maxFloor;
        this.currentFloor = minFloor;
        this.direction = Direction.IDLE;
        this.state = State.STOPPED;
    }

    public int getCurrentFloor() {
        return currentFloor;
    }

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

    public Direction getDirection() {
        return direction;
    }

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

    public State getState() {
        return state;
    }

    public void setState(State state) {
        this.state = state;
    }

    public int getMaxFloor() {
        return maxFloor;
    }

    public int getMinFloor() {
        return minFloor;
    }

    public boolean isValidFloor(int floor) {
        return floor >= minFloor && floor <= maxFloor;
    }
}

//请求队列
class RequestQueue {
    private LinkedList<Integer> internalRequests = new LinkedList<>();
    private LinkedList<Passenger> externalRequests = new LinkedList<>();

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

    public LinkedList<Passenger> getExternalRequests() {
        return externalRequests;
    }

    public void addInternalRequest(Integer floor) {
        internalRequests.add(floor);
    }

    public void addExternalRequest(Passenger passenger) {
        externalRequests.add(passenger);
    }
}

//控制类
class Controller {
    private Elevator elevator;
    private RequestQueue queue;

    public Controller(Elevator elevator, RequestQueue queue) {
        this.elevator = elevator;
        this.queue = queue;
    }

    public void processRequests() {
        determineDirection();
        System.out.println("Current Floor: " + elevator.getCurrentFloor() + " Direction: " + elevator.getDirection());
        while (!queue.getInternalRequests().isEmpty() || !queue.getExternalRequests().isEmpty()) {
            move();
            determineDirection();
        }
    }

    private void move() {
        Integer targetFloor = getNextFloor();
        if (targetFloor == null) {
            return;
        }

        if (elevator.getCurrentFloor() == targetFloor) {
            openDoors();
            removeRequests(elevator.getCurrentFloor());
            return;
        }

        elevator.setDirection(elevator.getCurrentFloor() < targetFloor ? Direction.UP : Direction.DOWN);

        while (elevator.getCurrentFloor() != targetFloor) {
            int nextFloor = elevator.getCurrentFloor() + (elevator.getDirection() == Direction.UP ? 1 : -1);
            elevator.setCurrentFloor(nextFloor);
            System.out.println("Current Floor: " + elevator.getCurrentFloor() + " Direction: " + elevator.getDirection());

            if (shouldStop(elevator.getCurrentFloor())) {
                openDoors();
                removeRequests(elevator.getCurrentFloor());
                targetFloor = getNextFloor();
                if (targetFloor == null) {
                    break;
                }
                elevator.setDirection(elevator.getCurrentFloor() < targetFloor ? Direction.UP : Direction.DOWN);
            }
        }

        if (targetFloor != null && elevator.getCurrentFloor() == targetFloor) {
            openDoors();
            removeRequests(elevator.getCurrentFloor());
        }
    }

    private Integer getNextFloor() {
        Integer internal = queue.getInternalRequests().peek();
        Passenger externalPassenger = queue.getExternalRequests().peek();
        Integer external = externalPassenger != null ? externalPassenger.getSourceFloor() : null;

        if (internal == null && external == null) {
            return null;
        }

        Direction currentDir = elevator.getDirection();
        int currentFloor = elevator.getCurrentFloor();

        if (currentDir == Direction.IDLE) {
            return getClosest(internal, external);
        }

        Integer candidate = null;
        if (currentDir == Direction.UP) {
            if (internal != null && internal > currentFloor) {
                candidate = internal;
            }
            if (external != null && external > currentFloor && externalPassenger.getDirection() == currentDir) {
                candidate = getClosest(candidate, external);
            }
            if (candidate == null) {
                candidate = getClosest(internal, external);
                if (candidate != null) {
                    elevator.setDirection(candidate > currentFloor ? Direction.UP : Direction.DOWN);
                }
            }
        } else {
            if (internal != null && internal < currentFloor) {
                candidate = internal;
            }
            if (external != null && external < currentFloor && externalPassenger.getDirection() == currentDir) {
                candidate = getClosest(candidate, external);
            }
            if (candidate == null) {
                candidate = getClosest(internal, external);
                if (candidate != null) {
                    elevator.setDirection(candidate > currentFloor ? Direction.UP : Direction.DOWN);
                }
            }
        }

        return candidate != null ? candidate : null;
    }

    private Integer getClosest(Integer a, Integer b) {
        if (a == null) return b;
        if (b == null) return a;
        int current = elevator.getCurrentFloor();
        return (Math.abs(a - current) < Math.abs(b - current)) ? a : b;
    }

    private boolean shouldStop(int floor) {
        if (!queue.getInternalRequests().isEmpty() && queue.getInternalRequests().peek() == floor) {
            return true;
        }

        Passenger passenger = queue.getExternalRequests().peek();
        if (passenger != null && passenger.getSourceFloor() == floor && passenger.getDirection() == elevator.getDirection()) {
            return true;
        }

        return false;
    }

    private void openDoors() {
        System.out.println("Open Door # Floor " + elevator.getCurrentFloor());
        System.out.println("Close Door");
    }

    private void removeRequests(int currentFloor) {
        Passenger passenger = queue.getExternalRequests().peek();
        if (passenger != null && passenger.getSourceFloor() == currentFloor && passenger.getDirection() == elevator.getDirection()) {
            queue.getInternalRequests().add(passenger.getDestinationFloor());
            queue.getExternalRequests().poll();
        }
        if (!queue.getInternalRequests().isEmpty() && queue.getInternalRequests().peek() == currentFloor) {
            queue.getInternalRequests().poll();
        }
    }

    private void determineDirection() {
        boolean hasUp = false;
        boolean hasDown = false;

        Integer internal = queue.getInternalRequests().peek();
        if (internal != null) {
            if (internal > elevator.getCurrentFloor()) {
                hasUp = true;
            } else if (internal < elevator.getCurrentFloor()) {
                hasDown = true;
            }
        }

        Passenger external = queue.getExternalRequests().peek();
        if (external != null) {
            int floor = external.getSourceFloor();
            Direction dir = external.getDirection();
            if (floor > elevator.getCurrentFloor() && dir == Direction.UP) {
                hasUp = true;
            } else if (floor < elevator.getCurrentFloor() && dir == Direction.DOWN) {
                hasDown = true;
            }
        }

        if (elevator.getDirection() == Direction.UP) {
            if (!hasUp && hasDown) {
                elevator.setDirection(Direction.DOWN);
            }
        } else if (elevator.getDirection() == Direction.DOWN) {
            if (!hasDown && hasUp) {
                elevator.setDirection(Direction.UP);
            }
        } else {
            if (hasUp || hasDown) {
                elevator.setDirection(hasUp ? Direction.UP : Direction.DOWN);
            }
        }
    }
}

public class Main {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        String data;
        int minFloor, maxFloor;
        List<String> list = new LinkedList<>();

        while (input.hasNext()) {
            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);
        RequestQueue requestQueue = new RequestQueue();

        for (int i = 2; i < list.size(); i++) {
            String request = list.get(i);
            if (request.contains(",")) {
                String[] parts = request.replaceAll("[<>]", "").split(",");
                int floor1 = Integer.parseInt(parts[0].trim());
                int floor2 = Integer.parseInt(parts[1].trim());
                requestQueue.addExternalRequest(new Passenger(floor1, floor2));
            } else {
                int floor = Integer.parseInt(request.replaceAll("[<>]", ""));
                requestQueue.addInternalRequest(floor);
            }
        }

        Controller controller = new Controller(elevator, requestQueue);
        controller.processRequests();

        input.close();
    }
}

代码分析

方法和类过于庞大和复杂,而代码注释过少

心得

第一次迭代,立刻将所有对队列的处理都改成符合先入先出的原则了,顺利通过了测试样例一,但是又在第二个测试样例上翻了车,为了

踩坑心得

题目集05

  • 1

  • 2

  • 3

public void removeRequests(int currentFloor) {
// 移除内部请求
internalRequests.removeIf(f -> f == currentFloor);
// 移除外部请求
externalRequests.removeIf(req -> req.getFloor() == currentFloor);
}

当时以为通过了样例测试一就已经大功告成了,然而第一次的算法连先入先出都没有完全遵守,到达楼层后将请求队列所有符合标准的都移除了,通过测试样例一只是因为碰巧样例一简单没有考查到罢了,最后还是没有通过测试点

题目集06
- 1
样例1
输入:
1
20
❤️,DOWN>
<2>
<5,UP>
<1>
<4,DOWN>
<3>
<2,DOWN>
<8>
<4>
END

- 2
输出:
Current Floor: 1 Direction: UP
Current Floor: 2 Direction: UP
Open Door # Floor 2
Close Door
Current Floor: 3 Direction: UP
Open Door # Floor 3
Close Door
Current Floor: 2 Direction: DOWN
Current Floor: 1 Direction: DOWN
Open Door # Floor 1
Close Door
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
Current Floor: 8 Direction: UP
Open Door # Floor 8
Close Door
Current Floor: 7 Direction: DOWN
Current Floor: 6 Direction: DOWN
Current Floor: 5 Direction: DOWN
Current Floor: 4 Direction: DOWN
Open Door # Floor 4
Close Door
Current Floor: 3 Direction: DOWN
Current Floor: 2 Direction: DOWN
Open Door # Floor 2
Close Door

这是补充的测试样例上翻了车,没有考虑到内外请求队列队首距离相等且均需要变换方向时候的目标楼层选择,还需要多多理解老师上课讲的如何设置测试样例和单目调试

题目集07

  • 1
    • 输入样例2:
      在这里给出一组输入。例如:

      1
      20
      <5,9>
      <8>
      <9,3>
      <4>
      <2>
      end

  • 2
    • 输出样例2:
      在这里给出相应的输出。例如:

      Current Floor: 1 Direction: UP
      Current Floor: 2 Direction: UP
      Current Floor: 3 Direction: UP
      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
      Current Floor: 8 Direction: UP
      Open Door # Floor 8
      Close Door
      Current Floor: 9 Direction: UP
      Open Door # Floor 9
      Close Door
      Current Floor: 8 Direction: DOWN
      Current Floor: 7 Direction: DOWN
      Current Floor: 6 Direction: DOWN
      Current Floor: 5 Direction: DOWN
      Current Floor: 4 Direction: DOWN
      Open Door # Floor 4
      Close Door
      Current Floor: 3 Direction: DOWN
      Current Floor: 2 Direction: DOWN
      Open Door # Floor 2
      Close Door
      Current Floor: 3 Direction: UP
      Current Floor: 4 Direction: UP
      Current Floor: 5 Direction: UP
      Current Floor: 6 Direction: UP
      Current Floor: 7 Direction: UP
      Current Floor: 8 Direction: UP
      Current Floor: 9 Direction: UP
      Open Door # Floor 9
      Close Door
      Current Floor: 8 Direction: DOWN
      Current Floor: 7 Direction: DOWN
      Current Floor: 6 Direction: DOWN
      Current Floor: 5 Direction: DOWN
      Current Floor: 4 Direction: DOWN
      Current Floor: 3 Direction: DOWN
      Open Door # Floor 3
      Close Door

  • 3

第二次迭代后完成了逻辑的改正,顺利通过了大部分测试样例,但是在样例测试二,第二次到达九楼的时候却连续开关了两次,可能是因为在转换方向后清除队列请求的部分有问题

改进建议

题目集05

  • 提取公共逻辑:在 determineInitialDirection、determineNextTarget 和 findClosestInCurrentDirection 等方法中,存在一些相似的逻辑,比如查找最近楼层的逻辑,可以将这些逻辑提取成一个单独的方法,以提高代码的复用性
  • 输入验证:当前代码对输入格式进行了简单验证,但可以进一步完善,例如在 Integer.parseInt 时添加异常处理,避免因输入非数字导致程序崩溃
  • 代码结构:可以将一些复杂的逻辑拆分成更小的方法,使每个方法的职责更加单一,提高代码的可读性和可维护性(不过题目要求是这样的)
  • 注释补充:代码注释仅在方法前和少部分地方,需要提高代码注释量,提高代码可读性

题目集06

  • 注释补充:复杂逻辑(如 getNextFloor 中的方向判断)缺乏注释,难以理解
  • 公共方法:getNextFloor() 和 determineDirection() 中存在重复的方向判断逻辑,可以将如getClosest通用化,传入多个目标列表

题目集07

  • 减少不必要的循环:在 move 方法中,while 循环会不断检查是否到达目标楼层,可优化为一次性计算需要移动的楼层数,减少循环次数
  • 输入验证:在 main 方法中,对用户输入的楼层范围和请求信息没有进行充分的验证,可能会导致程序崩溃。可以添加输入验证逻辑,确保输入的楼层在合法范围内
  • 方法拆分:一些方法的功能过于复杂如move方法,可将其拆分成更小的方法,提高代码的可维护性

总结

  • 通过这三次电梯系统迭代作业的实践,我在面向对象编程思想、类设计原则及复杂逻辑调度等方面有了一定的提升,同时我也深刻体会到软件开发中逐步优化与测试驱动的重要性
  • 这三次电梯作业如同 “渐进式通关”,从 “能运行” 到 “设计合理” 再到 “逻辑健壮”,每一次迭代都是对我的问题抽象能力和代码架构能力的挑战。通过不断踩坑(如队列操作错误、方向判断遗漏)、调试(单步跟踪请求处理流程)、重构(按职责拆分类),面向对象设计是复杂系统的基石:合理的类设计能大幅降低逻辑复杂度,反之则会导致代码臃肿难维护。测试是逻辑正确性的保障:仅凭样例一无法覆盖所有场景,需主动构造边界测试用例,甚至模拟错误输入验证系统鲁棒性。编程是持续优化的过程:没有 “完美” 的代码,只有不断迭代的设计。从最初的 “完成功能” 到后续的 “优化结构”“提升性能”,每一次改进都能带来新的认知
  • 未来我将继续加强设计模式的学习(如策略模式实现不同调度算法),并通过实际项目实践巩固 “模块化、可扩展” 的开发思维,逐步向高质量代码迈进
posted @ 2025-04-20 23:45  晚诉  阅读(25)  评论(0)    收藏  举报