0% found this document useful (0 votes)
35 views

Section 5: Data Structures & Algorithms

This section discusses implementation of queues using arrays and linked lists. It describes: 1) Implementing a queue sequentially using a one-dimensional array, with front and rear pointers. 2) Implementing a queue using a linked list, with front and rear pointers pointing to the first and last nodes. 3) Important problems in computer science that can be solved using queues, like job scheduling and graph traversal.

Uploaded by

YT Premone
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
35 views

Section 5: Data Structures & Algorithms

This section discusses implementation of queues using arrays and linked lists. It describes: 1) Implementing a queue sequentially using a one-dimensional array, with front and rear pointers. 2) Implementing a queue using a linked list, with front and rear pointers pointing to the first and last nodes. 3) Important problems in computer science that can be solved using queues, like job scheduling and graph traversal.

Uploaded by

YT Premone
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 12

Data Structures & Algorithms

SECTION 5

OBJECTIVES: At the end of the session, the student is expected to be able to


1. discuss implementation issues pertaining to the sequential representation of a queue
using one-dimensional array.
2. discuss implementation issues in relation to the linked representation of a queue using a
linked linear list.
3. describe some important problems in Computer Science whose solution requires the use
of a queue.
4. assess the suitability of using a queue to solve a given problem on the computer.

DISCUSSION:

The queue, which is next of kin to the stack, is another simple but important data
structure used in computer algorithms. Queues find applications in such diverse tasks as job
scheduling in operating systems, topological sorting, graph traversal, etc.

Abstractly, we can think of a queue as a linearly ordered set of elements on which is


imposed the discipline of “first-in, first-out.” The two basic operations defined on a queue are
insertion at the rear and deletion at the front of the queue. In certain applications, the elements
of a queue may be recorded according to certain priorities assigned to the elements
independently of their order of arrival. For instance, the rule may be “largest-in, first-out” instead
of “first-in, first-out”. We will not be concerned with such priority queues in this session.

deletion insertion
(here only) (here only)
a1 a2 a3 . . . ai ai+1 . . . an-1 an
The Queue as an ADT

There are two simple ways of implementing the ADT queue as a concrete data structure,
namely:

1. as a sequentially allocated one-dimensional array or vector


2. as a linked list

Sequential representation of a Queue

The figure below shows the sequential representation of a queue in a vector Q of size n.
The elements marked X comprise the queue, with the pointers front and rear indicating the front

Section 5 Page 1 of 12
Jennifer Laraya-Llovido
Data Structures & Algorithms

and rear of the queue. We adopt the convention that front points to the cell immediately before
the front element of the queue while rear points to the actual rear element of the queue.

front rear

1 2 3 4 5 6 7 8 9 . . . . . . n-1 n
Q(1:n) x x x x x x x x x x

Deletion | Insertion |

To initialize the queue, we set front Å rear Å 0. To insert an element, say x, into the
queue, we carry out these steps

rear Å rear + 1
Q(rear) Å x

and to delete the front element of the queue and store in the variable x, we perform the steps

front Å front + 1
x Å Q(front)

Note that as insertions and deletions are done, the queue moves rearward. Once rear =
n, we get a “full” queue, and an insertion operation results in an overflow condition, although
there may still be empty cells in the forward portion of S. Moving the elements of the queue
forward creates free space at the rear allowing insertion to proceed. If front catches up with the
rear, i.e., if front = rear, then we get an empty queue. Reinitializing the queue at this point will
give more room for subsequent insertions.
The EASY procedure INSERT-QUEUE, MOVE-QUEUE and DELETE-QUEUE
implement the operations described above.

procedure INSERT-QUEUE( Q, n, front, rear, x )


//inserts element x into queue//
array Q(1:n)
if rear = n then call MOVE-QUEUE( Q, n , front, rear )
rear ← rear + 1
Q(rear) < -- x
end INSERT-QUEUE
Section 5 Page 2 of 12
Jennifer Laraya-Llovido
Data Structures & Algorithms

procedure MOVE-QUEUE( Q, n, front, rear )


//moves queue element forward to make room at the rear//
array Q(1:n)
if front = 0 then [ output ‘No more available space’; stop ]
else [ for I <-- front + 1 to n do
Q(I-front) <-- Q(i)
endfor
rear <-- rear – front
front <-- 0 ]
end MOVE-QUEUE

procedure DELETE-QUEUE( Q, n, front, rear )


//deletes front queue element and stores in x//
array Q(1:n)
if front = rear then call QUEUE-EMPTY
else [ front < -- front + 1; x <-- Q(front)
if front = 0 then front <-- rear <-- 0 ]
end DELETE-QUEUE

As with stacks, an underflow condition results if we try to delete an element from an


empty queue. Note that such a condition is not necessarily abnormal. For instance, in operating
systems which use queue to implement input/output buffers, a process which attempts to delete
an item from an empty buffer simply enters a waits state until another process inserts an item
into the buffer, at which point the suspended process resumes and completes its operation
normally. Procedure QUEUE-EMPTY is coded subject to considerations such as this.

The need to move the queue elements forward to make room at the rear by invoking
MOVE-QUEUE can be eliminated altogether if we consider the cells to be arranged implicitly in
a circle to form a circular queue. In a manner analogous to that of the ‘straight’ queue, we adopt
the convention that front is one cell counterclockwise from the front element of the queue, while
rear points to the actual rear element of the queue.

Section 5 Page 3 of 12
Jennifer Laraya-Llovido
Data Structures & Algorithms

2 3
1 4
x x
x x
n 5
x x

n-1 6
x
.
x 7
.

.
8

9
.
. .

To initialize the queue, we set front <-- rear <-- 0. To insert an element, say x, into the
queue, we carry out these steps

rear <-- rear + 1


Q(rear) <-- x

and to delete the front element of the queue and store in the variable x, we perform the steps

front <-- front + 1


x <-- Q(front)

We use the modulo function in lieu of performing an if test in incrementing rear and front
primarily for brevity. Note that as insertions and deletions are done, the queue moves in a
clockwise direction. If front catches up with the rear, i.e., if front = rear, then we get an empty
queue. If rear catches up with front, a condition is also indicated by front = rear, then all cells are
in use and we get a full queue. In order to avoid having the same relation signify two different
conditions, we will not allow rear to catch up with front by considering the queue as full when

Section 5 Page 4 of 12
Jennifer Laraya-Llovido
Data Structures & Algorithms

exactly one free cell remains. Thus, a full queue is indicated by the relation front=(rear mod
n)+1.

The LAST procedures INSERT-CQUEUE and DELETE-CQUEUE implement the


insertion and deletion operations for a circular queue according to the conventions described
above.

procedure INSERT-CQUEUE( Q, n, front, rear, x )


//inserts element x into circular queue//
array Q(1:n)
if front = (rear mod n)+1 then [ output ‘No more available space’,stop]
else [rear <-- (rear mod n) +1;Q(rear) < -- x]
end INSERT-CQUEUE

procedure DELETE-CQUEUE( Q, n, front, rear, x )


//deletes the front element in circular queue and stores in x//
array Q(1:n)
if front = rear then call CQUEUE-EMPTY
else [ front < -- (front mod n)+ 1; x <-- Q(front)]
end DELETE-CQUEUE

Linked representation of a Queue

The figure below depicts a queue implemented as a linked linear list. The pointer
variable front points to the front element of the queue while the variable rear points to the rear
element of the queue. Two pointers are used so that both insertion and deletion operations can
be done in O(1) time. The condition front = Λ man the queue is empty.

INFO LINK rear


front
...

A queue represented as a linked linear list

Section 5 Page 5 of 12
Jennifer Laraya-Llovido
Data Structures & Algorithms

The EASY procedures INSERT-QUEUE and DELETE-QUEUE perform the insertion and
deletion operations for a queue represented as shown.

procedure INSERT-LQUEUE( front, rear, x )


//inserts element x into linked queue//
node( INFO, LINK )
call GETNODE( α )
INFO (α ) < - - x
LINK ( α ) < - - Λ
if front = Λ then front < - - rear < - - α
else [LINK(rear) < - - α ; rear < - - α]
end INSERT-LQUEUE

Two cases must be considered, namely, insertion into an initially empty queue and
insertion into an existing queue. The if statement performs the necessary test. The time
complexity of the procedure is clearly O(1).

procedure DELETE-LQUEUE( front, rear, x )


//deletes front element of linked queue and stores in x//
node( INFO, LINK )
if front = Λ then call LQUEUE-EMPTY
else [α < - - LINK(front); call RETNODE(α)]
end DELETE-LQUEUE

Unless LQUEUE-EMPTY(which is left unspecified and whose complexity is not known)


is invoked, the procedure clearly has time complexity O(1).

Topological sorting

We close this session with an interesting problem that arises in certain applications
involving activity networks. This is the topological sorting problem. The algorithm to solve the
problem contains a number of features that are particularly relevant to what we have studied tus
far:

Section 5 Page 6 of 12
Jennifer Laraya-Llovido
Data Structures & Algorithms

1. the use of both sequential and linked allocation techniques


2. the use of a linked queue embedded in a sequential vector
3. the use of simple techniques to reduce time complexity

Topological sorting, as a process, is applied to the elements of a set on which partial


ordering of a set, say S, as a relation between the elements of S (denoted by the symbol “≤” and
read as “precedes or equals”), satisfying the following properties for any elements x, y and z in
S:
1. Transitivity : if x ≤ y and y ≤ z, then x ≤ z.
2. Antisymmetry : if x ≤ y and y ≤ z, then x = y.
3. Reflexivity : x ≤ x.
If x ≤ y and x <> y, then we write x < y ( x ‘precedes’ y). An equivalent set of
properties to define partial order for this case is:

1. Transitivity : if x < y and y < z, then x < z.


2. Asymmetry : if x < y, then y < x.
3. Irreflexivity : x > x.

Some familiar examples of partial ordering from mathematics are : the relation x ≤ y
between real numbers x and y, and the relation u ⊆ v between sets u and v. Another example of
partial ordering is shown in the figure below (although you may not have thought of it in this light
before):

Section 5 Page 7 of 12
Jennifer Laraya-Llovido
Data Structures & Algorithms

CS 11

2 8

CS 12 CS 133

3 4 10

CS 13 CS 21 CS 22

5 6 11

CS 14 CS 30 CS 35

7 9

CS 110 CS 160
Figure 5.1 A partial ordering of 11 objects

The figure shows the prerequisite structure among the CS courses in the BCS
curriculum (Mathematics prerequisites are not shown and CS courses with prerequisites such
as junior standing, etc., are not included). This structure can be represented as a partial
ordering: CS 11 < CS 12, CS 12 < CS 13, CS 12 < CS 21, and so on. The relation CS 11 < CS
12, means, of course, that you must take and pass CS 11 before you can take CS 12.

To make the presentation more general, but utilizing figure above as vehicle for
exposition, let us imagine the figure as a partial ordering on n objects labeled 1 through n. In
relation 2 Æ 4, denoted by an arrow from object 2 to object 4, we say that 2 is a direct
predecessor of 4 and that 4 is a direct successor of 2. The topological sorting problem is to
arrange the objects in a linear sequence such that no object appears in the sequence before its
direct predecessor(s).

It is immediately obvious that there is, in general, more than one linear arrangement of
the objects satisfying this sorting rule. Two such are given below for partial ordering of the 11
objects in the figure above.
Section 5 Page 8 of 12
Jennifer Laraya-Llovido
Data Structures & Algorithms

1 8 2 4 3 10 6 5 11 7 9

1 8 2 3 4 5 6 7 9 10 11

Developing an algorithm to solve the topological sorting problem

We consider, in turn, the three main components of the desired solution, namely: the
form of the input, the form of the output and the algorithm proper.

INPUT = = > ALGORITHM PROPER = = > OUTPUT

1. Input: To communicate to the computer the partial ordering defined on the n


objects, we can input pairs of numbers of the form j, k for each relation j , k. For
instance, the complete input for the partial ordering shown in Figure 5-1 is 1,2;
2,3; 2,4; 3,5; 3,6; 4,6; 6,7; 7,9; 4,10; 8,10; 6,11; 8,11. Note that each pair j, k
corresponding to an arrow in the figure. The number pairs can be input in any
order.

2. Output: The output is a linear sequence of the objects (represented by their


integer labels) such that no object appears in the sequence before its
predecessors.

3. Algorithm Proper: According to the rule for topological sorting, an object can be
placed in the output only after its direct predecessor(s) have already been placed
in the output. One simple way to satisfy this requirement is to keep, for object, a
count of direct predecessors. An object whose count is zero (because it has no
direct predecessor) or whose count drops to zero(because its direct
predecessors have already been placed in the output) is ready for output. Once
such object is placed in the output, its direct successors are located so that their
count, in turn, can be decreased by 1. Locating the direct successors of a
particular object can be efficiently accomplished by maintaining, for each object,
a list of direct successors.
Section 5 Page 9 of 12
Jennifer Laraya-Llovido
Data Structures & Algorithms

The count of direct predecessors for each object can be stored in a one-
dimensional array, or vector, of size n, say COUNT(1:n). The list of direct
successors of each object, on the other hand, can be represented as a singly-
linked list, where each node has structure, say, (SUC,NEXT). The SUC field
contains the label of a direct successor, and the NEXT field contains a pointer to
the next successor, if any. The pointers to the n lists can be maintained in a one-
dimensional array of size n, say, TOP(1:n). Thus we use in a single algorithm
both sequential and linked allocation techniques.

Initially, the COUNT vector is set to zero and the TOP vector is set to null.
Subsequently, when an input pair of the form j, k arrives, the COUNT vector is
updated,

COUNT(k) < - - COUNT(k) + 1


and a new node for object k is added to the list of direct successors of object j.
So that the insertion can be done in O(1) time, the new node is added at the
head of the list:

call GETNODE(α)
SUC(α) < - - k
NEXT(α) < - - TOP(j)
TOP(j) < - - α

This is a very efficient technique which can be applied whenever it is not


necessary to keep the nodes of a list arranged in some specific order. We will
use this method in a number of algorithms on lists.

The figure below shows the COUNT and TOP vectors and the
corresponding linked lists generated by the input 1,2; 2,3; 2,4; . . . 6,11; 8,11. For
instance, we see that COUNT(6)=2 since object 6 has two direct predecessors,
namely, 3 and 4. Similarly, we see that the list of direct successors for object 6
consist of the nodes for objects 7 and 11, since object 6 is a direct predecessor
to both 7 and 11.(Can you explain why the node for object 7 comes after the
node for object 11 in the list?) This figure depicts graphically the internal
Section 5 Page 10 of 12
Jennifer Laraya-Llovido
Data Structures & Algorithms

representation in computer memory of the partial ordering defined on the 11


objects in Fig. 5.1 such that topological sorting can be carried out in a most
efficient manner.
To generate the output, which is a linear ordering of the objects such that no
object appears in the sequence before its predecessors, we proceed as follows:

a. Look for an object, say k, whose count of direct predecessors is zero, i.e.,
COUNT(k) = 0. Put k in the output.
b. Scan list of direct successors of k, and decrement the count of each such
successor by 1.
c. Repeat steps 1 and 2 until all objects are in the output.

To avoid having to go through the COUNT vector repeatedly as we look for


objects with a count of zero, we will constitute all such objects into a linked
queue. Initially, the queue will consist of objects with no direct predecessor (there
will always be at least one such object). Subsequently, each time that the count
of direct predecessors of an object drops to zero, the object is inserted into the
queue, ready for output. Since for each object, say j, in the queue, the count is
zero, we can now reuse COUNT(j) as a link field, appropriately renamed
QLINK(j), such that
QLINK(j) = k if k is the next object in the queue
= 0 if j is the rear element in the queue

We have embedded a linked queue in a sequential vector.

If the input to the algorithm is correct, i.e., if the input relations satisfy
partial ordering, then the algorithm terminates when the queue is empty with all
the objects placed in the output. If on the other hand, partial ordering is violated
such that there are objects which constitute one or more loops (for instance, 1<2;
2<3; 3<4; 4<1), then the algorithm still terminates, but objects comprising a loop
will not be placed in the output.

Section 5 Page 11 of 12
Jennifer Laraya-Llovido
Data Structures & Algorithms

The EASY procedure TOPOLOGICAL-SORT implements the algorithm.


procedure TOPOLOGICAL-SORT(n)
array COUNT(1:n), TOP(1:n)
node(SUC, NEXT)
equivalence COUNT, QLINK

//Initializations//
for k Å 1 to n do
COUNT(k) Å 0
TOP(k) Å
endfor
mÅn // m is the number of objects still to be output//

//Read in data and generate count of direct predecessors and lists of direct
successors//
input j, k
repeat
COUNT(k) Å COUNT(k) + 1
call GETNODE(α)
SUC(α) Å k
NEXT(α) Å TOP(j)
TOP(j) Å α
input j, k
until j = 0 //The pair 0,0 terminates the getting of input//

//Initialize output queue//


front Å 0
for k Å 1 to n do
if COUNT(k) = 0 then if front =0 then front Å rear Åk
else [QLINK(rear) Åk; rear Å k]
endfor

//Output objects//
repeat
output front
m Å m-1
α Å TOP (front)
while α <> null do
β Å SUC(α)
COUNT(β) Å COUNT(β) –1
if COUNT(β) = 0 then [QLINK(rear) Å β; rear Å β]
α Å NEXT(α)
endwhile
front Å QLINK(front)
until front = 0

//Check for presence of loops//


if m>0 then output “Some objects comprise a loop”
end TOPOLOGICAL-SORT

Section 5 Page 12 of 12
Jennifer Laraya-Llovido

You might also like