Stacks and Queues
Stacks and Queues
1. Stacks
In this handout, we will look at how lists are used. We can divide lists into two
basic categories, according to the operations that are required to operate on the
data in the list. These categories are stacks and queues.
A stack is a list of data structure that can only be accessed at one of its ends. In
other words, if we want to add a new value into the stack, or remove a value from
the stack, we can only do so from one end of the list. Consider Figure 1. To begin
with, in Figure 1a, the stack is empty. Then we push the value 6 onto the stack.
The term push is commonly used to refer to the operation of adding a value to a
stack data structure. Next we push the value 11 onto the stack, so 11 becomes the
‘top’ element of the stack, and 6 moves down to second place. In Figure 1c, we
pop an element from the stack. To pop an element from a stack simply means to
remove the top element. In this case, the value 11 is popped. Next the values 8 and
then 5 are pushed onto the stack, so in Figure 1f the top element is 5. Finally the
top element, 5, is popped from the stack. Stacks are often referred to as a LIFO
structure: Last In/First Out. You can think of stacks as similar to a pile of trays in
a cafeteria. Trays are put on the top of the pile and removed from the top. The last
tray put on the pile is the first one removed. A tray can only be taken if there are
trays in the pile, and a tray can be added to the pile only if there is enough room,
i.e. if the pile is not too high.
1
//*************** Function Calls.cpp ****************
// program to illustrate nested function calls
#include <iostream.h>
int f1 (int);
int f2 (int);
main () {
int x = 3;
cout << "f1(f2(3)) = " << f1(x) << endl;
}
int f1 (int a) {
int p = a * 3;
return f2(p + 2);
}
int f2 (int b) {
int q = b * 2;
return q - 1;
}
In the main function, the environment consists of the fact that the local variable x
has the value 3. So when the function f1 is called, this information is pushed onto
the stack. Next, inside f1, the environment contains the information that a is
equal to 3 and p is equal to 9, so when f2 is called this information is pushed onto
the stack. When execution of f2 finishes, the topmost environment is popped
from the stack, and when execution of f1 finishes, the next environment is
popped from the stack. In this way, the correct environment can be restored after
each function call completes. This process is illustrated in Figure 2.
2
2. Queues
A queue is also a type of list, but whereas with a stack the data elements are only
added and removed from the same end of the list, with a queue the elements are
always added to one end of the list, but removed from the other. You can think of
a queue data structure as being like a queue of people waiting to use a payphone,
or to be served at the bank. A queue is a FIFO structure: First In/First Out.
Queue operations are similar to stack operations. The following operations are
needed to properly manage a queue
clear() – Remove all elements from the queue.
isEmpty() – Check to see if there are elements in the queue.
enqueue(el) – Put the element el at the end of the queue.
dequeue() – Take the first element from the head of the queue.
firstEl() – Return the first element in the queue without removing it.
A series of enqueue and dequeue operations is shown in Figure 3. This time, the
new data elements are added to one end of the list (the right) and elements are
removed from the other end (the left). For example, after enqueueing 6 and 11, the
first dequeue operation (Figure 3c) removes the data element 6. If this were a
stack data structure, then the last element to be entered (11) would have been
removed. Similarly, at the dequeue in Figure 3f it is the 11 element that is
removed, as this is at the head of the queue.
3
such an implementation. Consider the sequence of enqueue and dequeue
operations illustrated in Figure 4. Here we are using an array of fixed length 5 to
implement the queue data structure. What happens when the final enqueue
operation (Figure 4h) is requested? There is no space left to add the new data
element at the end of the array, but there is free space at the beginning. These cells
should not be wasted. Therefore we can add the new data to the beginning of the
array. But now the beginning and end of the queue can potentially be anywhere in
the array, so we need to remember where they are. Figure 5a shows the array after
enqueueing the data element 2. The new element has been added to the beginning
of the list but the last variable has been updated to indicate this fact. Sometimes it
is easier to visualise such an array as a circular array, as in Figure 5b. Note that
this is for visualisation purposes only, it does not change the actual
implementation.
4
3. Deques
Figure 6 illustrates the operation of a deque. Note that we can think of a deque as
a generalisation of the stack and queue data structures. If we only use the
enqueueHead() and dequeueHead() operations then a deque behaves like a stack.
If we only use the enqueueHead() and dequeueTail() operations then it behaves
like a queue.
4. Priority Queues
Often, the basic queue data structure is not appropriate. For example, consider the
multitasking application described in Section 2 above. With the standard queue
data structure processes are executed by the CPU on a first-come-first-served
basis. Sometimes this is what is required. However, occasionally a process will
arrive at the end of the queue that is more urgent than the processes ahead of it.
For example, this may be an error-handling process. In this case, we want the
urgent process to jump the queue, and be executed before the existing processes in
the queue. In situations like this, a variation on the queue, called a priority queue,
is needed. In priority queues, elements arrive in an arbitrary order, but are
5
enqueued with information about their priority. Elements are dequeued according
to their priority and their current queue position.
Priority queues are usually implemented using dynamic data structures. A number
of different possible implementations exist, but we will not cover them in this
course.
Although implementations of lists, stacks, queues and other data structures have
been provided in these handouts, there is an existing C++ library of templated data
structures, called the Standard Template Library (STL). The STL contains built-in
classes for storing lists, stacks, queues, deques and priority queues. Many C++
textbooks contain details of the STL. For example, see “C++ Program Design” by
Cohoon & Davidson, p486.
6
Summary of Key Points