Data Structures AND Algorithms: Lecture Notes 9

You might also like

Download as pdf or txt
Download as pdf or txt
You are on page 1of 60

Bilgisayar Mühendisliği Bölümü

DATA STRUCTURES
AND
ALGORITHMS

Lecture Notes 9
Queues

Spring 2008

GIT – Computer Engineering Department


ROAD MAP
 Representing a waiting line, i.e., queue
 The methods of the queue ADT:
push, front, pop, empty, and size
 Implement the queue ADT:
– As an adapter of a the std::list
– Singly-linked list
– Circular array (a.k.a., circular buffer)
 The deque
 Applications of queues:
– Simulating physical systems with waiting lines ...
– Using Queues and random number generators

GIT – Computer Engineering Department Simulation - Lecture Notes 8 2


The Queue Abstract Data Type
 Visualization: queue = line of customers waiting for
some service
 In Britain, it is the common word for this
 Next person served is one who has waited longest:
– First-in, First-out = FIFO
 New elements are placed at the end of the line
 Next served is one at the front of the line

GIT – Computer Engineering Department Simulation - Lecture Notes 8 3


A Print Queue
 Operating systems use queues to
– Track tasks waiting for a scarce resource
– Ensure tasks carried out in order generated
 Print queue:
– Printing slower than selecting pages to print
– So use a queue, for fairness
– (Consider multiple queues, priorities, etc., later)
– A stack would be inappropriate
• Stacks are last-in, first-out (LIFO)
• Most recently selected document would print next
• Unless queue is empty, your job may never execute
... if others keep issuing print jobs
GIT – Computer Engineering Department Simulation - Lecture Notes 8 4
A Print Queue (continued)

GIT – Computer Engineering Department Simulation - Lecture Notes 8 5


Specification of the Queue ADT
Function Behavior
bool empty() const Returns true if the queue is empty;
otherwise returns false.
Item_Type& front(); Returns the object that is at the front of the
const Item_Type& front() const queue without removing it.
void pop() Removes the object at the front of the queue.
void push(const Item_Type&) Pushes an item onto the rear of the queue.
size_t size() const Returns the number of items in the queue.

GIT – Computer Engineering Department Simulation - Lecture Notes 8 6


Maintaining a Queue of Customers
Problem: Write a menu-driven program that:
 Maintains a list of customers waiting for service
 Can add a new customer at the end
 Can display name of next customer to serve
 Can display the length of the line

Analysis: Because service is in FIFO order, a queue is the


appropriate data structure
 Top level algorithm: while user not finished,
– Display menu and obtain choice, then
– Perform the selected operation
 The operations are all basic queue operations ...

GIT – Computer Engineering Department Simulation - Lecture Notes 8 7


Maintain_Queue.cpp
int main()
{
queue<string> customers;
string name;
int choice_num = 0;
string choices[] = {"push", "front", "pop", "size", "quit"};
const int NUM_CHOICES = 5;
// Perform all operations selected by user.
while (choice_num < NUM_CHOICES - 1) {
// Select the next operation.
cout << "Select an operation on customer queue\n";
for (int i = 0; i < NUM_CHOICES; i++) {
cout << i << ": " << choices[i] << endl;
}
cin >> choice_num;
switch (choice_num) {
. . .
} // End while.
return 0;
}

GIT – Computer Engineering Department Simulation - Lecture Notes 8 8


Maintain_Queue.cpp
case 0: // Insert a new customer
cout << "Enter new customer name\n";
cin >> name;
customers.push(name);
break;
case 1: // Display the Next Customer
cout << "Customer " << customers.front() << " is next in line\n";
break;
case 2: // Remove the Next Customer
cout << "Customer " << customers.front() << " removed from line\n";
customers.pop();
break;
case 3: // Display the Length of the Line
cout << "Size of line is " << customers.size() << endl;
break;
case 4:
cout << "Leaving.\n" << "Number of customers in queue is "
<< customers.size() << endl;
break;
default:
cout << "Invalid selection\n";
break;

GIT – Computer Engineering Department Simulation - Lecture Notes 8 9


ROAD MAP
 Representing a waiting line, i.e., queue
 The methods of the queue ADT:
push, front, pop, empty, and size
 Implement the queue ADT:
– As an adapter of a the std::list
– Singly-linked list
– Circular array (a.k.a., circular buffer)
 The deque
 Applications of queues:
– Simulating physical systems with waiting lines ...
– Using Queues and random number generators

GIT – Computer Engineering Department Simulation - Lecture Notes 8 10


Implementing Queue: adapter of std::list
 This is a simple adapter class, with following
mappings:
– Queue push maps to push_back
– Queue front maps front
– Queue pop maps to pop_front
– ...
 This is the approach taken by the C++ standard
library.
 Any sequential container that supports push_back,
front, and pop_front can be used.
 The list
 The deque

GIT – Computer Engineering Department Simulation - Lecture Notes 8 11


Implementing Queue: Singly-Linked List
This requires front and rear Node pointers:
template<typename Item_Type>
class queue {
. . .
private:
// Insert implementation-specific data fields
// Insert definition of Node here
#include "Node.h"
// Data fields
Node* front_of_queue;
Node* back_of_queue;
size_t num_items;
};

GIT – Computer Engineering Department Simulation - Lecture Notes 8 12


Using a Single-Linked List to Implement a Queue (continued)

GIT – Computer Engineering Department Simulation - Lecture Notes 8 13


Implementing Queue: Singly-Linked List (2)
 Insert at tail, using back_of_queue for speed
 Remove using front_of_queue
 Adjust size when adding/removing
– No need to iterate through to determine size

GIT – Computer Engineering Department Simulation - Lecture Notes 8 14


Implementing Queue: Singly-Linked List (3)
template<typename Item_Type>
void queue<Item_Type>::push(const Item_Type& item) {
if (front_of_queue == NULL) {
back_of_queue = new Node(item, NULL);
front_of_queue = back_of_queue;
} else {
back_of_queue->next = new Node(item, NULL);
back_of_queue = back_of_queue->next;
}
num_items++;
}

GIT – Computer Engineering Department Simulation - Lecture Notes 8 15


Implementing Queue: Singly-Linked List (4)
template<typename Item_Type>
Item_Type& queue<Item_Type>::front() {
return front_of_queue->data;
}

GIT – Computer Engineering Department Simulation - Lecture Notes 8 16


Implementing Queue: Singly-Linked List (4)
template<typename Item_Type>
void queue<Item_Type>::pop() {
Node* old_front = front_of_queue;
front_of_queue = front_of_queue->next;
if (front_of_queue == NULL) {
back_of_queue = NULL;
}
delete old_front;
num_items--;
}

GIT – Computer Engineering Department Simulation - Lecture Notes 8 17


Analysis of the Space/Time Issues
 Time efficiency of singly- or doubly-linked list good:
O(1) for all Queue operations
 Space cost:
– Doubly Linked List: ~3 words per item
– Singly Linked List: ~2 words per item
– Vector: ~1.5 words per item on average
• vector uses 1 word per item when fully packed
• 2 words per item when just grown
• On average ~1.5 words per item, for larger lists

GIT – Computer Engineering Department Simulation - Lecture Notes 8 18


Analysis of the Space/Time Issues (2)
 vector Implementation
– Insertion at end of vector is O(1), on average
– Removal from the front is linear time: O(n)
– Removal from rear of vector is O(1)
– Insertion at the front is linear time: O(n)

GIT – Computer Engineering Department Simulation - Lecture Notes 8 19


Implementing queue With a Circular Array
Basic idea: Maintain two integer indices into an array
 front: index of first element in the queue
 rear: index of the last element in the queue
 Elements thus fall at front through rear
Key innovation:
 If you hit the end of the array wrap around to slot 0
 This prevents our needing to shift elements around

 Still have to deal with overflow of space

GIT – Computer Engineering Department Simulation - Lecture Notes 8 20


Implementing Queue With Circular Array (2)

GIT – Computer Engineering Department Simulation - Lecture Notes 8 21


Implementing Queue With Circular Array (3)

GIT – Computer Engineering Department Simulation - Lecture Notes 8 22


Implementing Queue With Circular Array (4)

GIT – Computer Engineering Department Simulation - Lecture Notes 8 23


Implementing queue with a circular array
template<typename Item_Type>
class queue {
public:
. . .
private:
// Insert implementation-specific data fields
void reallocate();
// Data fields
static const size_t DEFAULT_CAPACITY = 10;
size_t capacity;
size_t num_items;
size_t front_index;
size_t rear_index;
Item_Type* the_data;
};

GIT – Computer Engineering Department Simulation - Lecture Notes 8 24


Implementing queue with a circular array(2)
template<typename Item_Type>
queue<Item_Type>::queue():capacity(DEFAULT_CAPACITY),
num_items(0),front_index(0),
rear_index(DEFAULT_CAPACITY - 1),
the_data(new Item_Type[DEFAULT_CAPACITY]) {}

GIT – Computer Engineering Department Simulation - Lecture Notes 8 25


Implementing queue with a circular array(3)
template<typename Item_Type>
void queue<Item_Type>::push(const Item_Type& item) {
if (num_items == capacity) {
reallocate();
}
num_items++;
rear_index = (rear_index + 1) % capacity;
the_data[rear_index] = item;
}

// Cost: O(1) (amortized)

GIT – Computer Engineering Department Simulation - Lecture Notes 8 26


Implementing queue with a circular array(4)
template<typename Item_Type>
Item_Type& queue<Item_Type>::front() {
return the_data[front_index];
}

template<typename Item_Type>
void queue<Item_Type>::pop() {
front_index = (front_index + 1) % capacity;
num_items--;
}

GIT – Computer Engineering Department Simulation - Lecture Notes 8 27


Reallocating a Circular Array

GIT – Computer Engineering Department Simulation - Lecture Notes 8 28


Implementing queue::reallocate
void queue<Item_Type>::reallocate() {
size_t new_capacity = 2 * capacity;
Item_Type* new_data = new Item_Type[new_capacity];
/*<snippet id="4" omit="false">*/
size_t j = front_index;
for (size_t i = 0; i < num_items; i++) {
new_data[i] = the_data[j];
j = (j + 1) % capacity;
}
/*</snippet>*/
front_index = 0;
rear_index = num_items - 1;
capacity = new_capacity;
std::swap(the_data, new_data);
delete[] new_data;
}

GIT – Computer Engineering Department Simulation - Lecture Notes 8 29


Comparing the Three Implementations
 All three are comparable in time: O(1) operations
 Linked-lists require more storage
– Singly-linked list: ~1 extra words / element
– Doubly-linked list: ~2 extra words / element
 Circular array: 0-1 extra word / element
– On average, ~0.5 extra word / element

GIT – Computer Engineering Department Simulation - Lecture Notes 8 30


The deque
 The deque is an abstract data type that combines
the features of a stack and a queue.
 The name deque is an abbreviation for double-
ended queue.
 The C++ standard defines the deque as a full-
fledged sequential container that supports random
access.

GIT – Computer Engineering Department Simulation - Lecture Notes 8 31


The deque class
Member Function Behavior
const Item_Type& Returns a reference to the element at position
operator[](size_t index)const; index.
Item_Type&
operator[](size_t index)
const Item_Type& Returns a reference to the element at position
at(size_t index) const; index. If index is not valid, the
Item_Type& out_of_range exception is thrown.
at(size_t index)
iterator insert(iterator pos, Inserts a copy of item into the deque at
const Item_Type& item) position pos. Returns an iterator that
references the newly inserted item.
iterator erase(iterator pos) Removes the item from the deque at
position pos. Returns an iterator that
references the item following the one erased.
void remove(const Item_Type& Removes all occurrences of item from the
item) deque.

GIT – Computer Engineering Department Simulation - Lecture Notes 8 32


The deque class (2)
Member Function Behavior
void push_front(const Item_Type& Inserts a copy of item as the first element of
item) the deque
void push_back(const Item_Type& Inserts a copy of item as the last element of
item) the deque.
void pop_front() Removes the first item from the deque.
void pop_back() Removes the last item from the deque.
Item_Type& front(); Returns a reference to the first item in the
const Item_Type& front() const deque.
Item_Type& back(); Returns a reference to the last item in the
const Item_Type& back() const deque.
iterator begin() Returns an iterator that references the
first item of the deque.
const_iterator begin() const Returns a const_iterator that references
the first item of the deque.

GIT – Computer Engineering Department Simulation - Lecture Notes 8 33


The deque class (3)
Member Function Behavior
iterator end() Returns an iterator that references one
past the last item of the deque.
const_iterator end() const Returns a const_iterator that references
one past the last item of the deque.
void swap(deque<Item_Type& other) Swaps the contents of this deque with the
other deque.
bool empty() const Returns true if the deque is empty.
size_t size() const Returns the number of items in the deque.
void resize(size_t new_size) Changes the size of the deque to
new_size. If new_size is smaller than the
current size, then items are removed from
the end. If new_size is larger than the
current size, then default valued items are
added to the end.

GIT – Computer Engineering Department Simulation - Lecture Notes 8 34


Implementing the Deque Using a Circular Array

 We can add the additional functions to the


circular array based queue.
– queue::push is the same as deque::push_back
– queue::pop is the same as deque::pop_front.
– Implementing deque::push_front and
deque::pop_back are left as exercises.
– Operator[] is implemented as follows:
Item_Type& operator[](size_t index){
return the_data[(index + front_index) % capacity];
}

GIT – Computer Engineering Department Simulation - Lecture Notes 8 35


The Standard Library Implementation
 The standard library uses a randomly
accessible circular array.
 Each item in the circular array points to a
fixed size, dynamically allocated array that
contains the data.
– The advantage of this implementation is that
when reallocation is required, only the pointers
need to be copied into the new circular array.

GIT – Computer Engineering Department Simulation - Lecture Notes 8 36


The Standard Library Implementation (2)

GIT – Computer Engineering Department Simulation - Lecture Notes 8 37


The index operator
template<typename Item_Type>
Item_Type& deque<Item_Type>::operator[](size_t i) {
if (i >= num_items)
throw std::out_of_range
("Invalid index to deque::operator[]");
int block_index = (offset + i) / BLOCK_SIZE;
int data_index = (offset + i) % BLOCK_SIZE;
return the_data[block_index][data_index];
}

GIT – Computer Engineering Department Simulation - Lecture Notes 8 38


The push_back functions
template<typename Item_Type>
void deque<Item_Type>::push_back(const Item_Type&
item) {
int capacity = the_data.size() * BLOCK_SIZE;
if ((num_items + offset) == capacity) {
the_data.push_back(new Item_Type[BLOCK_SIZE]);
}
num_items++;
(*this)[num_items - 1] = item;
}

GIT – Computer Engineering Department Simulation - Lecture Notes 8 39


The pop_front function
template<typename Item_Type>
void deque<Item_Type>::pop_front() {
offset++;
if (offset == BLOCK_SIZE) {
delete[] the_data.front();
the_data.pop_front();
offset = 0;
}
num_items--;
}

GIT – Computer Engineering Department Simulation - Lecture Notes 8 40


Simulating Waiting Lines Using Queues
 Simulation is used to study the performance:
– Of a physical (“real”) system
– By using a physical, mathematical, or computer
model of the system
 Simulation allows designers to estimate
performance
– Before building a system
 Simulation can lead to design improvements
– Giving better expected performance of the system

GIT – Computer Engineering Department Simulation - Lecture Notes 8 41


Simulating Waiting Lines Using Queues (2)

 Simulation is particular useful when:


– Building/changing the system is expensive
– Changing the system later may be dangerous
 Often use computer models to simulate “real”
systems
– Airline check-in counter, for example
– Special branch of mathematics for these
problems:
Queuing Theory

GIT – Computer Engineering Department Simulation - Lecture Notes 8 42


Simulate Strategies for Airline Check-In

GIT – Computer Engineering Department Simulation - Lecture Notes 8 43


Simulate Airline Check-In
 We will maintain a simulated clock
– Counts in integer “ticks”, from 0
 At each tick, one or more events can happen:
1.Frequent flyer (FF) passenger arrives in line
2.Regular (R) passenger arrives in line
3.Agent finishes, then serves next FF
passenger
4.Agent finishes, then serves next R passenger
5.Agent is idle (both lines empty)

GIT – Computer Engineering Department Simulation - Lecture Notes 8 44


Simulate Airline Check-In (2)
 Simulation uses some parameters:
– Max # FF served between regular passengers
– Arrival rate of FF passengers
– Arrival rate of R passengers
– Service time
 Desired output:
– Statistics on waiting times, agent idle time, etc.
– Optionally, a detailed trace

GIT – Computer Engineering Department Simulation - Lecture Notes 8 45


Simulate Airline Check-In (3)
 Design approach:
– Agent data type models airline agent
– Passenger data type models passengers
– 2 queue<Passenger>, 1 for FF, 1 for R
– Overall Airline_Checkin_Sim class

GIT – Computer Engineering Department Simulation - Lecture Notes 8 46


Simulate Airline Check-In (4)

GIT – Computer Engineering Department Simulation - Lecture Notes 8 47


Airline_Checkin_Sim Design
Data Field Attribute
Passenger_Queue The queue of frequent flyers.
frequent_flyer_queue
Passenger_Queue The queue of regular passengers
regular_passenger_queue
int frequent_flyer_max The maximum number of frequent flyers to
serve between regular passengers.
int max_processing_time The maximum time to serve a passenger.
int total_time The total time to run the simulation.
bool show_all A flag indicating whether to trace the
simulation.
int clock The current clock time
int frequent_flyers_since_RP The number of frequent flyers served since
the last regular passenger.
Function Behavior
void run_simulation() Controls the simulation.
void enter_data() Reads in the data for the simulation
void start_serve() Initiates service for a passenger.
void show_stats() Displays the summary statistics.

GIT – Computer Engineering Department Simulation - Lecture Notes 8 48


Passenger_Queue Design
Data Field Attribute
queue<Passenger> the_queue The queue of passengers
int num_served The number from this queue that were
served.
int total_wait The total time spent waiting by passengers
that were in this queue.
string queue_name The name of this queue.
double arrival_rate The arrival rate for this queue.
Function Behavior
Passenger_Queue(string Constructs a new queue with the specified
queue_name) name.
void check_new_arrival(int clock, Checks whether there was a new arrival for
bool show_all) this queue and, if so, inserts a passenger
into the queue.
int update(int clock, bool Updates the total waiting time and number
show_all) of passengers served when a passenger
from this queue is served.
int get_total_wait() const Returns the total waiting time.
int get_num_served() const Returns the number of passengers served.

GIT – Computer Engineering Department Simulation - Lecture Notes 8 49


Passenger Design
Data Field Attribute
int passenger_id The ID number for this passenger
int processing_time The time needed to process this passenger
int arrival_time The time this passenger arrives
static int max_processing_time The maximum time to process a passenger
static int id_num The sequence number for passengers
Function Behavior
Passenger(int arrival_time) Constructs a new passenger, assigns a
unique ID and the specified arrival time.
Computes a random processing time in the
range 1 to max_processing_time.
int get_arrival_time() const Returns arrival_time
int get_processsing_time() const Returns processing_time
static void Sets the max_processing_time.
set_max_processing_time(int
max_process_time)

GIT – Computer Engineering Department Simulation - Lecture Notes 8 50


The main function
int main() {
Airline_Checkin_Sim simulation;
simulation.enter_data();
simulation.run_simulation();
simulation.show_stats();
}

GIT – Computer Engineering Department Simulation - Lecture Notes 8 51


The enter_data function
Internal variable Attribute Conversion
frequent_flyer_queue.arrival_rate Expected number Divide input by 60
of frequent flyer to obtain arrivals
arrivals per hour. per minute.
regular_passenger_queue.arrival_rate Expected number Divide input by 60
of regular to obtain arrivals
passenger arrivals per minute.
per hour.
max_processing_time Maximum service None
time in minutes.
total_time Total simulation None.
time.
show_all Flag, if true If input begins with
display minute- 'y' or 'Y' set to
by-minute trace true.
of simulation.

GIT – Computer Engineering Department Simulation - Lecture Notes 8 52


The run_simulation function
void Airline_Checkin_Sim::run_simulation() {
for (clock = 0; clock < total_time; clock++) {
frequent_flyer_queue.check_new_arrival(clock, show_all);
regular_passenger_queue.check_new_arrival(clock, show_all);
if (clock >= time_done) {
start_serve();
}
}
}

GIT – Computer Engineering Department Simulation - Lecture Notes 8 53


The start_serve function
void Airline_Checkin_Sim::start_serve() {
if (!frequent_flyer_queue.empty()
&& ( (frequent_flyers_since_RP <= frequent_flyer_max)
|| regular_passenger_queue.empty())) {
// Serve the next frequent flyer.
frequent_flyers_since_RP++;
time_done = frequent_flyer_queue.update(clock, show_all);
}
else if (!regular_passenger_queue.empty()) {
// Serve the next regular passenger.
frequent_flyers_since_RP = 0;
time_done = regular_passenger_queue.update(clock, show_all);
}
else if (show_all) {
cout << "Time is " << clock
<< ": Server is idle\n";
}
}

GIT – Computer Engineering Department Simulation - Lecture Notes 8 54


Passenger_Queue::check_arrival
void Passenger_Queue::check_new_arrival(int clock, bool
show_all) {
if (my_random.next_double() < arrival_rate) {
the_queue.push(Passenger(clock));
if (show_all) {
cout << "Time is "
<< clock << ": "
<< queue_name
<< " arrival, new queue size is "
<< the_queue.size() << endl;
}
}
}

GIT – Computer Engineering Department Simulation - Lecture Notes 8 55


Passenger_Queue::update
int Passenger_Queue::update(int clock, bool show_all) {
Passenger next_passenger = the_queue.front();
the_queue.pop();
int time_stamp = next_passenger.get_arrival_time();
int wait = clock - time_stamp;
total_wait += wait;
num_served++;
if (show_all) {
cout << "Time is " << clock
<< ": Serving "
<< queue_name
<< " with time stamp "
<< time_stamp << endl;
}
return clock + next_passenger.get_processing_time();
}

GIT – Computer Engineering Department Simulation - Lecture Notes 8 56


Passenger Implementation
Passenger::Passenger(int arrive_time) {
arrival_time = arrive_time;
processing_time = 1 +
my_random.next_int(max_processing_time);
passenger_id = id_num++;
}

GIT – Computer Engineering Department Simulation - Lecture Notes 8 57


Concerning “Random” Numbers
 Not really random, but pseudo-random
– Generated by a definite algorithm
– Next produced from the last
– Thus, sequence determined by starting value
– Starting value is called the seed
– Seed usually set from date/time, but can set
directly to get same sequence on each occasion
• Good for testing!

GIT – Computer Engineering Department Simulation - Lecture Notes 8 58


Concerning “Random” Numbers (2)
 The C++ standard library contains the
functions rand and srand.
 These are declared in <cstdlib> and are the
same as used by C.
 The function rand generates an integer
between 0 and RAND_MAX.
 The function srand sets the starting value
(known as the seed).
– If the same seed is used, the same sequence of
random numbers is generated
GIT – Computer Engineering Department Simulation - Lecture Notes 8 59
The Random class
/** Class to encapsulate the standard random number generator. */
class Random {

public:

Random() {
std::srand(std::time(0));
}

Random(int seed) {
std::srand(seed);
}

int next_int(int n) {
return int(next_double() * n);
}

double next_double() {
return double(std::rand()) / RAND_MAX;
}
};

GIT – Computer Engineering Department Simulation - Lecture Notes 8 60

You might also like