Amazon Interview Question
Software Engineer / DevelopersCountry: United States
Interview Type: Phone Interview
how can you assume one elevator when the question asked is for multiple elevators?? you get on spot rejection there.
I think its safe to mention to the interviewer that you are making a scalable system. You can have 1 to many elevators but a single elevator manager instance.
You need to assume at least two, or else the system for setting elevators in motion will have no reason to exist. Assume that all elevators are at rest, at whatever floor. Now, someone outside the elevators presses a button. At this point, you would need to describe the method that chooses which elevator to set in motion--setting all elevators in motion would be entirely idiotic.
But if you're assuming only 1 elevator, then this question is irrelevant, and therefore never answered.
So you can't just assume one elevator, or you fail.
Interesting question. This
Let's solve
First examplify
Consider this scenario
There are 4 elevators in the Sky scraper
Each elevator either goes up or down
Each elevator can stop at any of the levels 0-320(it's the words tallest building)
Your man is at level 0
Write test case
E1 is at level10 and is going up
E2 is at level 100 and is going down
E3 is at level 200 and is going down
E4 is at level 69 and is going down
It is quiet obvious from the above that E4 will be the first to arrive
So for each elevator we have to consider up down and level this would give method signature
Public elevatorNode pollElevator( int elevatorno)
Public void elevatorNode(Boolean direction,int level)
A monitor that polls each elevator based on a timer based on round robin fashion will give closest elevator
Public sychronized void getClosest()
// poll each node using wait notify
// add each node level and elevatorno with boolean true for down to map
// sort map by level
// Get value at 0 return elevator number
While it's not "quiet [sic] obvious" that E4 will be first, due to the fact that E1 could reach its last stop at floor 12 or so and then head back downwards, I do agree that this is the most correct design I can think of.
(there's really nothing you can do to account for E1; even if the "last floor pressed" is 12, someone could get picked up at floor 11 and press floor 300, so unless you want to do a state-based check every [small t time], which defeats half the point of the object orientation, assigning the pickup at F0 to E1 would be irresponsible.)
class ElevatorManager
{
Elevator elevatorArray[ELEVATOR_COUNT];
// List of pending hallcalls
HallCallList hallcallList_;
public:
// User API to intiate a hall call
void ProcessHallCall(const HallCall& rHallCall);
private:
// Starts the Hall Call Process Thread
void StartHallCallProcessThread();
// Create thread for each elevator to make it operational
void StartCarMovementThread();
};
void ElevatorManager::ProcessHallCall(const HallCall& rHallCall)
{
hallcallList_->AddHallCall(rHallCall);
}
ElevatorManager::StartHallCallProcessThread()
{
HallCallProcessThread thread(elevator, hallcallList_);
thread.run();
thread.detach();
}
ElevatorManager::StartCarMovementThread()
{
for (int i = 0; i < ELEVATOR_COUNT; i++)
{
ElevatorThread thread(elevatorArray[i]);
thread.run();
thread.execute();
}
}
//////////////////////////////////////////////////////////////////////////////////
// class encapsulating the hallcall list treatment
class HallCallList
{
list<HallCall> lHallCallList_;
Mutex mHallCallListMutex;
public:
void AddHallCall(HallCall hallCall);
HallCall PopHallCall();
};
HallCallList::AddHallCall(HallCall hallCall)
{
MutexLock(mHallCallListMutex);
lHallCallList_->push_back(hallCall);
hallCallListSemaphore->signal();
}
HallCall HallCallList::PopHallCall()
{
MutexLock(mHallCallListMutex);
Hallcall hallcall = lHallCallList_->front();
lHallCallList_->erase(lHallCallList_.begin(), lHallCallList_.begin());
return hallcall;
}
//////////////////////////////////////////////////////////////////////////////////
// Creates one thread per hallcall so that non-interfering hall calls can work parallely
HallCallProcessThread::Execute()
{
hallCallListSemaphore->signal();
MutexLock(mHallCallListMutex);
Hallcall hallcall = hallcallList_->pop();
OneHallcallThread thread(hallcall, hallcallList_);
thread.run()
thread.detach();
}
OneHallcallThread::Execute()
{
bool bCallProcessed = false;
// Sorted Elevator Array sorts the provided elevator array based on the current requested hallcall floor.
// We want to try out the elevator which is nearest
SortedElevator elevatorArray(elevatorArray, hallcall);
FOR_EACH elevator in elevatorArray
{
if (true == elevator->ProcessCall(hallcall))
{
bCallProcessed = true;
break;
}
}
// If currently no elevator can process the hallcall
// it is added back to the hallcalllist
if (false == bCallProcessed)
{
hallcallList_->AddHallCall(hallcall);
}
}
/////////////////////////////////////////////////////////////////////////////////////////////
class Elevator
{
typedef int FLOOR;
typedef list<FLOOR> DestFloorList;
Mutex elevLock;
Semaphore destFloorEmptySem; // To put elevator thread into wait state when no hallcall
DestFloorList lDestFloorList_;
public:
Elevator() : elevLock(1), destFloorEmptySem(0);
void ProcessCall(Hallcall hallcall);
void Move();
protected:
void Reset();
void UpdateCurrentFloor();
};
Elevator::ProcessCall(HallCall hallcall)
{
Mutexlock lock(elevLock);
// Logic to find if the elevator can process the request
if ((this->state == STATIONARY) ||
((this->state == UP) && (hallcall.direction == UP) && (this->currFloor < hallcall.floor)) ||
((this->state == DOWN) && (hallcall.direction == DOWN) && (this->currFloor > hallcall.floor)))
{
this->lDestFloorList_->push_back(hallcall.floor);
this->destFloorEmptySem_->signal();
return true;
}
return false;
}
Elevator::Reset()
{
this->state = STATIONARY;
this->currFloor = 0;
this->lDestFloorList_ = empty List;
}
Elevator::UpdateCurrentFloor()
{
Mutexlock lock(this->elevLock);
Floor targetFloor = this->lDestFloorList_->front();
if (this->currFloor > targetFloor)
{
this->state = DOWN;
this->currFloor--;
}
else if (this->currFloor < nextFloor)
{
this->state = UP;
this->currFloor++;
}
}
ElevatorThread::Execute()
{
elevator->Move();
}
Elevator::Move()
{
this->Reset();
while(true)
{
if ((this->state == UP) || (this->state == DOWN))
{
floor = this->lDestFloorList_->top();
if (floor == this->currFloor)
{
{
Mutexlock lock(elevLock);
this->lDestFloorList_->pop_front();
this->state == STATIONARY;
}
this->destFloorEmptySem_->wait();
this->UpdateCurrentFloor();
}
else
{
this->UpdateCurrentFloor();
}
}
else
{
this->destFloorEmptySem_->wait();
this->UpdateCurrentFloor();
}
sleep(5);
}
}
Prior to code implementation, generally you should clarify
- How many floors the skyscraper has?
- How many elevator halls it has?
- How elevators are consistent of? (Normally, there should be different types of elevators like workers only, 0-10th floor, 10-20th floor, skip even number or something like)
- How about the max capacity? (How much weight)
or etc...
I tried the design of a single elevator and extended that to multiple elevators scenario. Given that i am not able put class diagrams here i have put it here
h%t%t%p://thought-works.blogspot.in/2012/11/object-oriented-design-for-elevator-in.html
(remove % in the url and paste in the address bar )
This is one of the interesting questions and discussion on this would benefit many folks like us who want to learn design.
We can create a state and event machine for each elevator in procedural design
elvator [ MAX_State] [Max_event] state are static , moving up and moving down and event are perssing of buttons (inside the lift and outside the lift) . We can write the call back functions for all the state and event machines .
Why is everyone writing a lot of code? This is a design question. Writing classes and explaining how they interact with each other is sufficient. Also why assume there are 4 elevators? Assume there is just one bidirection elevator and it supports basic operations, you can always make it fancier later.
- John Dalton September 21, 2012