29

This question was asked to me during company interview - Which data-structure is efficient for Implementing Elevator Mechanism?

I am not able to find the efficient data-structure for it even after a lot of Googling.

I can think of Priority queue to Implement it.Is priority queue an efficient data structure or more efficient data structure is there for implementing Elevator Mechanism?

Thanks!

Saurav Sahu
  • 13,038
  • 6
  • 64
  • 79
Ritesh Kumar Gupta
  • 5,055
  • 7
  • 45
  • 71
  • 2
    could you provide a link to something showing what "elevator mechanism" is? – andrew cooke Aug 17 '12 at 16:29
  • This has been answered before on [Stackoverflow.](http://stackoverflow.com/questions/493276/modelling-an-elevator-using-object-oriented-analysis-and-design) Basically there is no "most efficient" way, as it depends mostly on the exact applications and the complexity of the elevator's operations. – StuckAtWork Aug 17 '12 at 16:37
  • @StuckAtWork That's a different one - the question at your link talks about an elevator bank, not a single elevator. – Sergey Kalinichenko Aug 17 '12 at 16:42
  • 1
    It is much the same algorithm; Elevator object which queries a Request bank and acts accordingly. The only difference is there is no 'choosing' going on in the request side. – StuckAtWork Aug 17 '12 at 16:44
  • 4
    @StuckAtWork I disagree: the answer mentions a single "request list", letting the elevator encapsulate nearly all the details that are very relevant here. This is an oversimplification: a correct implementation of the elevator algorithm requires hundreds of lines of code, with nasty logic piled on top of even nastier logic. I saw an elevator problem offered for a programming contest at TopCoder - it was the first problem in their history that failed to get a single correct solution during the contest, even though some extremely strong programmers were competing there at the time. – Sergey Kalinichenko Aug 17 '12 at 16:53

6 Answers6

33

Since you cannot implement mechanisms in software (although you can certainly model them), I assume that the question has been about the Elevator algorithm.

The algorithm looks deceivingly simple, yet it is surprisingly very tough to implement, even with a good set of data structures in hand. A good structure to use for this algorithm is three priority queues:

  1. For the current direction with entries past the current point,
  2. For the opposite direction, and
  3. for the current direction prior to the current point.

Your implementation would first decide the direction, then pick a queue into which to place the requested pair of {from, to} values.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • 1
    is it a priority queue or tree like data structure. Because suppose you are at 3 and moving upwards than when you reach at 4 you need to search through the priority queue which is O(N). But if it is Tree then you can find in O(logN). Am i missing something here? – Trying Dec 10 '13 at 22:39
  • @Trying One priority queue is not going to do it - unless you are willing to search all requests on every floor that you pass (which is expensive) you need to separate the requests into multiple queues. – Sergey Kalinichenko Dec 10 '13 at 22:46
  • 1
    why can't i maintain two BitSets, one for upward moment and another form downward. no of bits is equal to no of floors. Suppose the lift is in 3rd floor than it need to just check for the corresponding bit if set than open else don't open. It is same for whether the button is pressed from outside or inside the elevator. Please share your input. Thanks. – Trying Dec 10 '13 at 22:49
  • @Trying Different implementations are definitely possible. Bitsets let you see if you need to open the door at the current floor, but to see if it's time to reverse the direction or stop you need to scan the rest of the bitset after each stop. Again, the best thing to do at this point is to try it out and see for yourself. The problem is surprisingly full of corner cases. – Sergey Kalinichenko Dec 10 '13 at 22:57
  • Than in that case we just need to have one min and max variable for downward and forward movement to get what is the minimum and maximum floor up to which we need to move. – Trying Dec 10 '13 at 23:14
  • @Trying Then you will need to maintain the state of these two variables as well - a liability when users are allowed to cancel calls. – Sergey Kalinichenko Dec 11 '13 at 00:24
  • @dasblinkenlight Can you please explain your above three priority queues uses by taking an example. – devsda Feb 26 '14 at 11:40
  • 1
    @dasblinkenlight why do we need 3 heaps? I can understand inserting new requests into max-heap or min-heap depending on current point. I don't see the purpose of the third heap. – allenylzhou Apr 26 '14 at 20:55
  • @dasblinkenlight Why 3 lists are needed ? I think 2 lists will suffice. – Aditya Joshee Aug 10 '16 at 04:02
14

What if we used two linked list one for upward direction movement requests and another for downward direction movement request.

e.g

First request: a).while lift is on 0th floor and a requests come for 3rd floor.

linked list for upward movement.

3->null.

linked list for downward movement.

null.

Second request: b). while lift has moved to 1st floor and a request comes from 2nd floor for upward movement.

linked list for upward movement.

2->3->null

linked list for downward movement.

null.

Third request: c) Suppose 2 persons enter on the 2nd floor, one presses button for 5th floor and another for 1st.

linked list for upward movement.

3->5->null

Note: Here 2 has been deleted from upward linked list as it has been reached.

linked list for downward movement.

1->null.

d)Suppose a person enters on 3rd floor and presses button for 0th floor

linked list for upward movement.

5->null

linked list for downward movement.

1->0->null.

Once 5th floor iis reached upward requests linked list becomes empty so downwards linked list can be considered for movement.

If both the linked list are empty then elevator would be at rest.

So i think linked list can also be an effective data structure for elevator

sacrishi
  • 137
  • 1
  • 5
  • 1
    Suppose the elevator stops at 3rd after coming from 0th floor. After stopping at 3rd both lists are empty. Now 2 people enter at 3rd. I has to go to 5th and other to 1st. But button '1' is pressed before button '5'. Now the elevator should first go to 1st floor and then 5th. Your logic will not cover this. – Aditya Joshee Aug 10 '16 at 04:16
  • @AdityaJoshee Have a look into my algorithm...you may get to see it being implemented.. please let me know otherwise. :) – Saurav Sahu Sep 13 '16 at 17:16
  • @AdityaJoshee you can simple give the priority or choose which one is closest by subtracting the desired floor num from current floor , whichever is min, choose that list :). I liked this solution – minigeek Jul 19 '17 at 11:25
  • Why do you need a linked list? The request should be server in optimal manner not in FIFO manner. Consider this - User A requested for floor 5th from 1st floor then a user comes in at 2nd floor and wants to go to 4th floor. An array instead of linkedlist will be an optimal data structure for this. – Terminal May 07 '19 at 06:55
5

Below is one way to design Elevator System. Each elevator uses Queue (it could be Blocking Queue) to store floor requests. Also there is a central ElevatorManager which monitors all Elevator queues and it can delegate requests to a certain elevator depending upon some business rules. It's the job of ElevatorManager to efficiently delegate requests to the relevant elevator. Below pseudocode does not optimize the delegation algorithm but it shows how actual delegation could be done to a list of elevators.

Classes needed for Elevator System:

ElevatorManager [Singleton - This is the main elevator program which will manage n elevators in the building]
Members:
List of Elevator
Queue of Floor.Request // This maintains request for both directions. One improvement could be to keep two queues, one for each direction but it would increase complexity
MIN_FLOOR
MAX_FLOOR
Operations:
delgate()
halt() // set whole elevator system in maintenance mode or halt operation


Elevator [Represents individual elevators. There could be n elevators in a building]
Members:
Queue of Floor // this needs to be sorted so may be a PriorityQueue could be used
Direction : Enum [Enum of direction - up, down, wait, idle, maintenance]
CurrentFloor : Floor
Operations:
operate()
moveUp()
moveDown()
openDoor()
closeDoor()
callEmergencyLine()
getDirection()
getCurrentFloor()
setInMaintenanceMode()


Floor [Represents individual floors]
Members:
eNum of Floors
class Request {
currentFloor
destinationFloor
Direction [Up, Down]
}
Operation:
goUp()
goDown()

Some of the main pseudocode for above components:

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
    }
}

It's also availabe here on my Github: https://github.com/prabhash1785/Java/blob/master/ObjectOrientedDesign/src/com/prabhash/java/design/objectoriented/elevator/ElevatorDesignWithPseudocode.md

Prabhash Rathore
  • 709
  • 10
  • 18
3

How about having an array, where each array entry represents a floor. When user wanted to stop to a floor he will mark that entry in the array, and elevator will look into array and clear the entry if it marks when elevator reaches to that floor. Similar to SCAN/CSAN scheduling algorithm. I look forward for your comments

rgaut
  • 3,159
  • 5
  • 25
  • 29
2

For simplicity, let us consider a single elevator system.

Data Structure used: Simple lists to store floor#, enums for event and state.

The system can be made Event-State driven. Every aspect of the users behavior or environment has to be taken care in deciding what all scenarios can be thrown at the elevator.

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. 

Using the above mentioned rules, the elevator continues its usual work.

Saurav Sahu
  • 13,038
  • 6
  • 64
  • 79
0

Consider you are the senior program developer, lift manufacture company approaches your team to do a program on lift operation with following constraints.

  1. Building has total 15 floors, and 4 underground floors for car parking
  2. Initially the lift starts from -4th floor and reaches the destination on customer wish
  3. Later the lift starts from its last position and reaches the destination
  4. If the lift operator press halt (eg. Press 2 to halt) the reaches to -4th floor from its last position. Sample output:
  5. Start
  6. Halt

1

Enter your destionation floor: 3 Lift starts…

-3

-1 0 1 2 3

Satish
  • 1