电梯机构的数据结构

时间:2012-08-17 16:25:15

标签: algorithm design-patterns data-structures

在公司采访中我问过这个问题 - 哪种数据结构对实施电梯机制有效?

即使经过大量谷歌搜索,我也无法找到有效的数据结构。

我可以想到优先级队列来实现它。优先级队列是一个有效的数据结构还是更有效的数据结构用于实现电梯机制?

谢谢!

5 个答案:

答案 0 :(得分:29)

由于你无法在软件中实现机制(虽然你当然可以建模),但我认为这个问题一直是Elevator algorithm

该算法看起来很简单,但实现起来却非常困难,即使手头有很好的数据结构。用于此算法的良好结构是三个优先级队列:

  1. 对于条目超过当前点的当前方向,
  2. 反方向,
  3. 表示当前点之前的当前方向。
  4. 您的实现将首先确定方向,然后选择一个队列,将所请求的{from, to}值对放入其中。

答案 1 :(得分:14)

如果我们使用两个链表1进行向上方向移动请求而另一个用于向下方向移动请求怎么办。

<强> e.g

第一次请求: a)。当电梯在0楼,请求来到3楼。

向上移动的链表。

3→。空

向下移动的链表。

空。

第二次请求: B)。当电梯移到一楼时,请求从二楼向上移动。

向上移动的链表。

2→3→空

向下移动的链表。

空。

第三次请求: c)假设有2人进入二楼,一人按下第五层按钮,另一人按第一层。

向上移动的链表。

3→5→空

注意:此处的2已从上传链表中删除。

向下移动的链表。

1→。空

d)假设一个人进入三楼并按下0楼的按钮

向上移动的链表。

5→空

向下移动的链表。

1→0→。空

一旦到达5楼iis,请求链表变为空,因此可以考虑向下链表进行移动。

如果链接列表都为空,则电梯将处于静止状态。

所以我认为链表也可以是电梯的有效数据结构

答案 2 :(得分:3)

以下是设计电梯系统的一种方法。每个电梯使用队列(可能是阻塞队列)来存储楼层请求。还有一个中央ElevatorManager,它监控所有Elevator队列,它可以根据一些业务规则将请求委托给某个电梯。 ElevatorManager的工作是将请求有效地委托给相关的电梯。伪代码下面没有优化委托算法,但它显示了如何对电梯列表进行实际委派。

电梯系统所需的课程:

ElevatorManager [Singleton - 这是管理建筑物内n部电梯的主要电梯程序]
    成员:
        电梯清单
        Floor.Request //这维护了对两个方向的请求。一个改进可能是保留两个队列,每个方向一个,但会增加复杂性         MIN_FLOOR
        MAX_FLOOR
    操作:
        delgate()
        halt()//将整个电梯系统设置为维护模式或停止运行


电梯 [代表个别电梯。建筑物中可能有n部电梯     成员:
        楼层队列//需要对其进行排序,因此可以使用PriorityQueue         方向:枚举[方向枚举 - 向上,向下,等待,空转,维护]
        CurrentFloor:地板
    操作:
        操作()
        为moveUp()
        下移()
        执行Opendoor()
        closeDoor()
        callEmergencyLine()
        getDirection()
        getCurrentFloor()
        setInMaintenanceMode()


楼层 [代表个别楼层]
    成员:
        enum of Floors
        类请求{
            currentFloor
            destinationFloor
            方向[向上,向下]
        }
    操作:
        党团()
        货仓()

以上组件的一些主要伪代码:

class Floor {
    goUp() {
        ElevatorManager.queue.offer(new Request(currentFloor, destinationFloor, up));
    }   

    goDown() {
        ElevatorManager.queue.offer(new Request(currentFloor, destinationFloor, down));
    }
}

ElevatorManager {
    delegate() {

        // Instead of using one object, we could use a list to track idle and elevators moving in same direction so that these list could be used for next requests in queue
        // but again to simplify pseudocode, I am using single objects instead of lists
        Elevator idleElevator; // track idle elevator
        Elevator elevatorMovingInSameDirection; // elevator moving in same direction as next request in main elevator manager queue 

        while(!halt()) { //keep delegating until powered down or whole system is halted through main controls

            if(queue.peek() != null) {

                Request req = queue.peek();
                boolean startAgain = false; // flag to start from beginning if the request is already pushed to one of the elevators queue during iterating elevators

                for(Elevator elevator : elevators) {

                    // first find if there is an elevator at current floor going in same direction as current request in queue
                    if(req.currentFloor == elevator.currentFloor && req.direction == elevator.direction) {
                        elevator.queue.offer(req.destinationFloor);
                        queue.poll(); // remove this request from Elevator Manager queue

                        startAgain = true;
                        break;
                    }

                    // check if this elevator is idle
                    if(elevator.direction == "idle")) {
                        idleElevator = elevator; // For this simple design, I am ok to overwrite idle elevator value and instead get the latest idle elevatior
                    }

                    // check if this elevator is moving in desired direction and elevator's current floor is behind desired floor in queue
                    if(elevator.direction == req.direction) {

                        // Make sure elevators moving in same direction should also be behind the floor where request is made
                        if(req.direction == "Up" && req.currentFloor - elevator.currentFloor > 0) {

                            elevatorMovingInSameDirection = elevator; // Same as above, it's ok to get this overwritten and instead get the latest elevator moving in same      direction
                        }

                        // Make sure elevators moving in same direction should also be behind the floor where request is made
                        if(req.direction == "Down" && req.currentFloor - elevator.currentFloor < 0) {
                            elevatorMovingInSameDirection = elevator;
                        }
                    }

                }

                // Only delegate to other floors if you could not find elevator going in same direction at same floor from where the request was made
                if(!startAgain && idleElevator != null) {
                    idleElevator.queue.offer(req.destinationFloor);
                    queue.poll();
                }

                // if we could neither find elevator at current floor nor idle elevator then send this request to elevator behind current Floor and moving in same direction as the request
                if(!startAgain && elevatorMovingInSameDirection != null) {
                    elevatorMovingInSameDirection.queue.offer(req.destinationFloor);
                    queue.poll();
                }


            }
        }
    }
}


Elevator {

    moveUp() {
        this.currentFloor += 1;
    }

    moveDown() {
        this.currentFloor -= 1;
    }

    operate() {

        while(queue.peek() != null) {

            Floor nextFloorInQueue = queue.peek();

            while(this.currentFloor != nextFloorInQueue.request.destinationFloor) {
                if(this.direction == "Up") {
                    moveUp();
                } else if(this.direction == "down") {
                    moveDown();
                }
            }

            queue.poll(); // remove the request from queue
            open(); //open door

            Direction backUpDirection = this.direction; //back up elevators direction to retrieve it later once dooor closes
            this.direction = "idle"; // set state to idle to let elevatorManager know that requests at current floor could be offered to this elevator queue

            Thread.sleep(10000); // sleep for 10 seconds so that people can leave elevator

            close(); // once people are out close door to move to next floor in queue
            this.direction = backUpDirection;
        }

        this.direction = "idle"; // once queue is empty set the direction to idle
    }
}

这也可以在我的Github上找到:https://github.com/prabhash1785/Java/blob/master/ObjectOrientedDesign/src/com/prabhash/java/design/objectoriented/elevator/ElevatorDesignWithPseudocode.md

答案 3 :(得分:0)

如何拥有一个数组,其中每个数组条目代表一个楼层。当用户想要停在地板上时,他会在阵列中标记该条目,如果电梯到达该楼层,电梯将查看阵列并清除该条目。与SCAN / CSAN调度算法类似。我期待着你的意见

答案 4 :(得分:0)

为简单起见,让我们考虑一下 电梯系统。

使用的数据结构:简单列表用于存储楼层#,枚举用于事件和状态。

可以使系统事件状态驱动。必须注意用户行为或环境的每个方面,以决定在电梯上可以抛出的所有场景。

Events of the elevator : GOINGUP, GOINGDOWN, STOP 
States of the elevator : ONMOVE, WAITING (between door open and close), IDLE (serving no one), UNDERMAINTENANCE

Elevator movement is usually driven by two activites:
1. Press Up or Down key (before the entry gate of elevator) and wait for the elevator to come and serve you. (Press-And-Wait, say PAW) 
2. Enter inside the elevator and make request by pressing keys (Enter-And-Request, say EAR)

So it can said that PAW from outside and EAR from inside can decide the hops of the elevator. But what about direction?

Two possible types of PAW: PAWup (press Up button) and PAWdown (press Down button)

Now, EAR can be any of the three types depending upon the users behavior. These are the critical challenges in deciding the algorithm: 
1.) Normal - same direction as PAW (wanted to go down and enter lower floor#) 
2.) Opposite - wanted to go down BUT enter higher floor#
3.) Indecisive - Do Nothing, no key press

Now comes all the important rules:
RULE 1: If at IDLE, use FCFS to decide between the DownList front and UpList front - whoever is oldest, serve it first to ensure less waiting time. 
RULE 2: When both lists (DownList and UpList) are empty, move elevator to IDLE state. 
RULE 3: Elevator state change GOINGUP->STOP clears that floor# from UpList. Similarly, GOINGDOWN->STOP clears that floor from DownList.
RULE 4: Absolute Zero Skipping: GOINGxxx serves as many floors in xxxList as possible. 
RULE 5: Elevator doesn't favour Opposite-EAR, and obviously can't serve Indecisive-EAR.
RULE 6: Elevator in UNDERMAINTENANCE state is shunned from all external signals.
RULE 7: In the event of Power-cuts or Fire, the elevator goes and stops at Lobby. Flooding??

To achieve RULE#5, GOINGDOWN clears all the lower floor# in DownList in ONE GO. Similarly, GOINGUP clears all the higher floor# in UpList.    

Lets discuss one scenario to clear the above concepts:
Say, an elevator is resting at floor 7 is at IDLE state, 
    DownList : 
    UpList : 

IDLE@7 - PAWdown@12 then PAWup@9 then PAWdown@13
    DownList : 12, 13 (older requests at lower index.Push new requests at front.)
    UpList : 9 
    Using RULE#2, in the above case, 
    Event: GOINGUP to Pick@12.  

WAITING@12  - 12 cleared following RULE#3

MeanWhile, PAWup@8 then PAWup@10 then PAWdown@10, so updated lists are:
    DownList : 13, 10 
    UpList : 9, 8, 10

So here, in the current situation, if the EAR is
1.) Normal, GOINGDOWN(towards new EAR) is triggered.
2.) Opposite/Indecisive, GOINGDOWN(towards 9) is triggered and add the new EAR in UpList. 

使用上述规则,电梯继续其正常工作。