You are on page 1of 58

INTRODUCTION TO

DATA STRUCTURE Lecture 4

Queue and list implementation

Dr Mourad Raafat
Motivation: Why Queue? First In First Out (FIFO)
• In a queue,
– new values are always added at the rear of the list
– values are removed from the opposite end of the list, the
front

Input D C B A Output

• Examples of queues
– checkout at supermarket
– Toll Station
• Car comes, pays, leaves
– Check-out in Big super market
• Customer comes, checks out and leaves
– More examples: Printer, Office Hours, …
© Waleed A. Yousef 2008 2
E.g., Printing Queue

• A.doc, B.doc, C.doc arrive to printer.

Now printing A.doc


C B A

A.doc is finished. Now printing B.doc


C B

D.doc comes D C B Now still printing B.doc

B.doc is finished. Now printing C.doc


D C

C.doc is finished. Now printing D.doc


D

© Waleed A. Yousef 2008 3


Array Implementation1: Physical Model
(Front is fixed as in physical lines)

• Shifting all items to front in the array when


dequeue operation. ( Too Costly… )

• Why this was not a problem in the array


implementation of Stacks?

n-1 3 2 1 0 A leaves n-1 3 2 1 0


…… C B A …… C B

rear=2 front=0 rear=1 front=0

© Waleed A. Yousef 2008 4


Array Implementation2: Linear Model
(Two indices, front and rear)

n-1 3 2 1 0
D C B A

Max_Size rear front

After A leaves,
n-1 3 2 1 0
D C B

Max_Size rear front

© Waleed A. Yousef 2008 5


The problem is that there will be many empty places in the
front of array and rear is always incremented.

n-1 3 2 1 0
D C B A

Max_Size rear front

After A,B,C,D leave


n-1 3 2 1 0
K J I H G F E

Max_Size
rear front

© Waleed A. Yousef 2008 6


Array Implementation 3: Circular Implementation
0 Max-1
1


Fr

ar
on

Re
t
Occupied

Circular Queue
Max-1
0 Front Rear
2
… Occupied …
Unwinding

Front Rear
Occupied
0 1 … …
Max-1

Linear
© Waleed A. Yousef 2008
Implementation 7
Checking the Boundary conditions
Rear and Front advance in this direction

0 MAX-1
Queue with two elements
F R

Queue with one element


FR

With Empty condition:


Next to Rear = Front. R F

Queue with one element


FR

F R
Full Condition:
Next to Rear = Front
Therefore we waste one location R F 8
© Waleed A. Yousef 2008
Better solution: Use indicator variable to
distinguish between Empty and Full conditions.
Rear and Front advance in this direction

Empty queue:
Next to Rear = Front.
Size=0 R F

R F

Full queue:
Next to Rear = Front.
Size=MAX R F

9
Again: Definitions, where every thing should start from!

Definition: type is a set of values and a set of operations on


those values.

Example: We can define a datatype boolean that takes the set


of values {0, 1} together with the operations AND, OR, and NOT.

Example: int, in C, is the set consisting all of the integers


between INT_MIN (-(215  1)) and INT_MAX (215 – 1),
which are defined in the header file limits.h

Definition: A Sequence of length 0 is empty. A sequence of


length n  1 of elements from a set T is an ordered pair (Sn-
1,t) where Sn-1 is a sequence of length n-1 of elements from T,
and t is an element of T.

© Waleed A. Yousef 2008 10


Definition: A queue of elements of type T is a finite sequence of
elements of T together with the following operations:

1. Create the queue, leaving it empty.


2. Determine whether the queue is empty or not
3. Determine whether the queue is full or not
4. Find the size of the queue
5. Append a new entry onto the top of the queue, provided the
queue is not full.
6. Retrieve the front entry in the queue, provided the queue is not
empty.
7. Serve (and remove) the front entry from the queue, provided
the queue is not empty.
8. Traverse the queue, visiting each entry
9. Clear the queue to make it empty

11
User Level
typedef struct queue{ (interface)
int front;
int rear; void main(){
int size;
QueueEntry entry[MAXQUEUE]; Queue q;
}Queue;

front

rear

size

entry
12
void CreateQueue(Queue *pq){ User Level
pq->front= 0; (interface)
pq->rear = -1;
void main(){
pq->size = 0;
}
//Initializing front =5 and rear Queue q;
=4 will work if MAXQUEUE >=6. But,
since MAXQUEUE can be 1 we CreateQueue(&q);
intialize as above.
MAXQUEUE-1
}

front
&q
rear pq
size
0
entry
13
void Append(QueueEntry e, Queue* pq){
pq->rear = (pq->rear + 1) % MAXQUEUE;
pq->entry[pq->rear] = e;
pq->size++;
}

MAXQUEUE-1

front
&q
pq rear

size
0
© Waleed A. Yousef 2008
entry 14
void Serve(QueueEntry *pe, Queue* pq){
*pe = pq->entry[pq->front];
pq->front = (pq->front + 1) % MAXQUEUE;
pq->size--;
}

MAXQUEUE-1

front
&q
pq rear

&e size
pe
0
© Waleed A. Yousef 2008
entry 15
int QueueEmpty(Queue* pq){
return !pq->size;
}

int QueueFull(Queue* pq){


return (pq->size == MAXQUEUE);
}

MAXQUEUE-1

front

rear

size
0
© Waleed A. Yousef 2008
entry 16
int QueueSize(Queue* pq){
return pq->size;
}
void ClearQueue(Queue* pq){
pq->front = 0;
pq->rear = -1;
pq->size = 0;
}//same as CreateQueue. No nodes to free.

MAXQUEUE-1

front

rear

size
0
© Waleed A. Yousef 2008
entry 17
void TraverseQueue(Queue* pq, void (*pf)(QueueEntry)){
int pos, s;
for(pos=pq->front, s=0; s<pq->size; s++){
(*pf)(pq->entry[pos]);
pos=(pos+1)%MAXQUEUE;
}
}

MAXQUEUE-1

front

rear

size
0
© Waleed A. Yousef 2008
entry 18
Linked Queues(to overcome fixed size limitations):
Just get the idea now, do not worry about details.
q.rear q.front q.rear q.front

Node
q
Empty Queue Queue of size 1

pn

New
node

First In Second in Last in

q.rear q.Front How to insert a node


19
Linked Queues(to overcome fixed size limitations):
Just get the idea now, do not worry about details.

How to serve a node

Last in

First In Second in Before Last

q.rear q.Front

pn

20
Type Definition
New
node

First In Second in Last in

typedef struct queuenode{


QueueEntry entry;
struct queuenode *next;
}QueueNode;

typedef struct queue{


QueueNode *front;
QueueNode *rear;
int size; //old trick
}Queue;
21
Implementation level User Level
(what really happens) (interface)
void CreateQueue(Queue *pq){ void main(){
pq->front=NULL;
pq->rear=NULL; Queue q;
pq->size=0;
} CreateQueue(&q);

}
CreateQueue
&q
pq

q
22
void Append(QueueEntry e, Queue* pq){
QueueNode*pn=(QueueNode*)malloc(sizeof(QueueNode));
pn->next=NULL;
pn->entry=e;
User Call:
Queue q;
QueueEntry e;
pq->rear->next=pn;
pq->rear=pn; Append(e, &q);
e
pq->size++; pn
}
&q
e
pq

First In Second in Last in

23
Always take care of special cases (queue is empty)
void Append(QueueEntry e, Queue* pq){
QueueNode*pn=(QueueNode*)malloc(sizeof(QueueNode));
pn->next=NULL;
pn->entry=e;
if (!pq->rear)
pq->front=pn;
else
pq->rear->next=pn;//run time error for empty queue
pq->rear=pn;
pq->size++;
}

&q pn
pq
e

© Waleed A. Yousef 2008 24


void Serve(QueueEntry *pe, Queue* pq){
QueueNode *pn=pq->front;
*pe=pn->entry;
pq->front=pn->next;
free(pn);

Last in
pq->size--;
}

First In Second in Before last

&q
pq pn

© Waleed A. Yousef 2008 25


Always take care of special cases: Only one element exists

void Serve(QueueEntry *pe, Queue* pq){


QueueNode *pn=pq->front;
*pe=pn->entry;
pq->front=pn->next;
free(pn);
if (!pq->front)
pq->rear=NULL;
pq->size--;
}
Last in

&q
pq pn

© Waleed A. Yousef 2008 26


Implementation level User Level
(what really happens) (interface)
int QueueEmpty(Queue* pq){ void main(){
return !pq->front;
} Queue q;

int QueueFull(Queue* pq){ CreateQueue(&q);


return 0;
}
}
int QueueSize(Queue* pq){
return pq->size;
}
&q
pq

q 27
© Waleed A. Yousef 2008
void ClearQueue(Queue* pq){
while(pq->front){
pq->rear=pq->front->next;
free(pq->front);
pq->front=pq->rear;
}
pq->size = 0;
}/*Moving with two pointers,
Exactly as in LinkedStacks*/ Last in

First In Second in Before last

&q
pq
© Waleed A. Yousef 2008 28
void TraverseQueue(Queue* pq, void(*pf)(QueueEntry)){
for(QueueNode *pn=pq->front; pn; pn=pn->next)
(*pf)(pn->entry);
}

Last in

First In Second in Before last

&q
pq
pn
© Waleed A. Yousef 2008 29
Very important note for all linked structures. E.g., in Queues:
In Push and Append we have to check for exhausted memory.
The code can be modified to:

int Append(QueueEntry e, Queue* pq){


QueueNode*pn=(QueueNode*)malloc(sizeof(QueueNode));
if (!pn)
return 0;
/*This is much better than the Error message of
the book because this is more flexible. Also,
the same function for contiguous implementation
has to return 1 always to have consistent
interface*/
else{
//Put here exactly all of the remaining code
return 1;
} ...;
If (!Append(e, &q)){
...;
© Waleed A. Yousef 2008
} 30
Comparison between the array-based and the linked
implementation: “Which is always better?” is a wrong question!
Do it yourself as a homework.

© Waleed A. Yousef 2008 31


Application of Queues: Simulation of an Airport
Read it if you like
Simulation is the use of one system to imitate the behavior of
another system. A computer simulation is a program to imitate
the behavior of the system under study.

1. The same runway is used for both landings and takeoffs.


2. One plane can land or take off in a unit of time, but not both.
3. A random number of planes arrive in each time unit.
4. A plane waiting to land goes before one waiting to take off.
5. The planes that are waiting are kept in queues landing and
takeoff, both of which have a strictly limited size.

© Waleed A. Yousef 2008 32


Motivation: Why Lists?
• In a general list:
– new values are added in position determined by the user.
– Element is removed from a position determined by the user.

• Important notice:
– if we keep adding and removing from the first position (the
head of the list) the general list will behave as a stack.

– If we keep adding from one end and removing from another


end the list will behave as a queue.

• Application:
– In queues, sometimes we need a priority for some elements.
We may need to put an emergency call prior to others.

© Waleed A. Yousef 2008 33


Motivation: Why Lists?
ST-1
ze-1 XLI
0 si MA

• This is a list (just a logical view, no implementation yet) with


number of entries equals to size
• We can add a new element in position 0  p  size.
• However, we delete from 0  p  size-1.
• Now, let us be rigorous and define lists.

© Waleed A. Yousef 2008 34


Definition: A general list of elements of type T is a finite
sequence of elements of T together with the following operations:

1. Create the list, leaving it empty.


2. Determine whether the list is empty or not
3. Determine whether the list is full or not
4. Find the size of the list.
5. Insert a new entry in the position 0  p  size.
6. Delete an entry from the position 0  p  size-1
7. Traverse the list, visiting each entry
8. Clear the list to make it empty

© Waleed A. Yousef 2008 35


0 size-1 MAXLIST-1

void InsertList(int p, ListEntry e, List *pl);


Precondition:
1- The list pl has been created.
2- not full
3- 0 p size.
Postcondition:
1- e has been inserted at position p
2- all elements at old positions p, p+1, …, size-1 are incremented by 1.
3- size increases by 1.

void DeleteList(int p, ListEntry *pe, List *pl);


Precondition: The list pl has been created, not empty, and 0  p  size-1.
Postcondition: e has been retrieved from position p, and all elements at
old positions p+1, …, size-1 are decremented by 1. size decreases by 1.

© Waleed A. Yousef 2008 36


0 size-1 MAXLIST-1

void RetrieveList(int p, ListEntry *pe, List *pl);


same precondition as DeleteList. And the list is unchanged

void ReplaceList(int p, ListEntry e, List *pl);


same precondition. And the element is replaced

Other functions have similar pre- and post-conditions to the Queue


and Stack.

Now let us start the Contiguous (array-based) Implementation

© Waleed A. Yousef 2008 37


MAXLIST-1
/*List.h*/

#include "Global.h"

typedef struct list{ size -1


ListEntry entry[MAXLIST];
int size;
}List;

void CreateList (List *);


int ListEmpty (List *);
int ListFull (List *);
int ListSize (List *); 0
void DestroyList (List *);
void InsertList (int, ListEntry, List *); size entry
void DeleteList (int, ListEntry *, List *);
void TraverseList(List *, void (*Visit)(ListEntry));
void RetrieveList(int , ListEntry *, List *);
void ReplaceList (int, ListEntry, List *);
© Waleed A. Yousef 2008 38
void CreateList(List *pl){
MAXLIST-1
pl->size=0;
} // (1)

int ListEmpty(List *pl){


return !pl->size;
size -1
} // (1)

int ListFull(List *pl){


return pl->size==MAXLIST;
} // (1)

int ListSize(List *pl){


0
return pl->size;
} // (1) size entry

void DestroyList(List *pl){


pl->size=0;
} // (1)
© Waleed A. Yousef 2008 39
/*0 <= p <= size*/
void InsertList(int p, ListEntry e, List *pl){
int i;
/*The loop shifts up all the elements in
the range [p, size-1] to free the pth
location*/
for(i=pl->size-1; i>=p; i--)
pl->entry[i+1]=pl->entry[i];
pl->entry[p]=e;
pl->size++; MAXLIST-1
} // (n)

Special Cases are all the combination of the following: size -1


p = 0 or p = size
p
size = 0
All the cases will work p -1
0
size entry
Inserting one element requires too many shifting!!
© Waleed A. Yousef 2008 40
/*0<= p <= size-1 and List not empty*/
void DeleteList(int p, ListEntry *pe, List *pl){
int i;
*pe=pl->entry[p];
/*The loop shifts down all the elements in
the range [p+1, size-1] to free the pth
location*/
for(i=p+1; i<=pl->size-1; i++)
pl->entry[i-1]=pl->entry[i];
pl->size--; MAXLIST-1
} // (n)
size -1
Special Cases are all the combination of the following:
p = 0 or p = size - 1 p +1
p
size = 1
All the cases will work
0
size entry
Deleting one element requires too many shifting!!
© Waleed A. Yousef 2008 41
/* 0<= p <= size-1*/
void RetrieveList(int p, ListEntry *pe, List *pl){
*pe=pl->entry[p];
} // (1)
/* 0<= p <= size-1*/
void ReplaceList(int p, ListEntry e, List *pl){
pl->entry[p]=e;
} // (1)

void TraverseList(List* pl, void (*Visit)(ListEntry)){


int i;
for(i=0; i<pl->size; i++) MAXLIST-1
(*Visit)(pl->entry[i]);
} // (n) size -1

p +1
p

0
size entry
© Waleed A. Yousef 2008 42
0 size-1 MAXLIST-1

Issues at the user level:

How to insert at the beginning of the List?


InsertList(0, e, &l);

How to insert at the end of the List?


InsertList(ListSize(&l), e, &l);

How to use it as a stack?


How to use it as a queue?
Think at home

Now let us start the linked Implementation

© Waleed A. Yousef 2008 43


/*List.h*/
#include "Global.h"

typedef struct listnode{


ListEntry entry;
struct listnode *next;
}ListNode;

typedef struct list{


ListNode *head;
int size;
}List;

p=0 p=1 p=size-1

size head

© Waleed A. Yousef 2008 44


void CreateList(List *pl){
pl->head=NULL;
pl->size=0;
} // (1)

int ListEmpty(List *pl){


return (pl->size==0);
//or return !pl->head
} // (1)

int ListFull(List *pl){


return 0;
} // (1)

int ListSize(List *pl){


return pl->size;
} // (1)

0 1 p … size-1
size head
© Waleed A. Yousef 2008 45
void DestroyList(List *pl){
ListNode *q;
while(pl->head){
q=pl->head->next;
free(pl->head);
pl->head=q;
}
pl->size=0;
}//we took it before many times: // (n)

void TraverseList(List* pl, void(*Visit)(ListEntry)){


ListNode *p=pl->head;
while(p){
(*Visit)(p->entry);
p=p->next;
}
} // (n)

0 1 p … size-1
size head
© Waleed A. Yousef 2008 46
void InsertList(int pos, ListEntry e, List *pl){
size
ListNode *p, *q;
int i; head
p=(ListNode *)malloc(sizeof(ListNode));
p->entry=e;
0
p->next=NULL;

if (pos==0){//will work also for head equals NULL


p->next=pl->head; pos-1
pl->head=p;
} q
else{
for(q=pl->head, i=0; i<pos-1; i++) pos
q=q->next;
p
p->next=q->next;
q->next=p;


} 1
pl->size++;
} // (n) but without shifting elements.
size-1

© Waleed A. Yousef 2008 47


We should make sure that the memory is not full when we call malloc
(as we did previously). We have to design the function to return int
not void and we modify the contiguous implementation accordingly to
return 1 always.

© Waleed A. Yousef 2008 48


int InsertList(int pos, ListEntry e, List *pl){
size
ListNode *p, *q;
int i; head
if (p=(ListNode *)malloc(sizeof(ListNode))){
p->entry=e;
0
p->next=NULL;

if (pos==0){//works also for head = NULL


p->next=pl->head; pos-1
pl->head=p;
} q
else{
for(q=pl->head, i=0; i<pos-1; i++) pos
q=q->next;
p
p->next=q->next;
q->next=p;


}
pl->size++;
return 1;
} size-1
else return 0;
}
© Waleed A. Yousef 2008 49
void DeleteList(int pos, ListEntry *pe, List *pl){ size
int i; head
ListNode *q, *tmp;
0
if (pos==0){
*pe=pl->head->entry;
tmp=pl->head->next;
free(pl->head); 1
pl->head=tmp;
}// it works also for one node tmp
else{ pos-1
for(q=pl->head, i=0; i<pos-1; i++)
q=q->next; q
pos
*pe=q->next->entry;
tmp=q->next->next;
free(q->next);
pos+1
q->next=tmp;
}// check for pos=size-1 (tmp will be NULL)
tmp
pl->size--;
} //O(n) but without shifting elements. size-1

© Waleed A. Yousef 2008 50


size

head

void RetrieveList(int pos, ListEntry *pe, List *pl){


int i; 0
ListNode *q;
for(q=pl->head, i=0; i<pos; i++)
q=q->next; 1
*pe=q->entry;
}
pos-1

void ReplaceList(int pos, ListEntry e, List *pl){


int i; pos
ListNode *q;
for(q=pl->head, i=0; i<pos; i++)
q=q->next;
pos+1
q->entry=e;
}

size-1

© Waleed A. Yousef 2008 51


Comparison between the array-based and the linked
implementation: “Which is always better?” is a wrong question!

Array-based Linked
CreateList (1) (1)
InsertList (n) (n)
DeleteList (n) (n)
RetrieveList (1) (n)
ReplaceList (1) (n)

InsertList is very time consuming for Array-based because of


copying elements, especially if the elements are large records.

RetrieveList and ReplaceList are always better for contiguous


implementation.

Read the book very well.


© Waleed A. Yousef 2008 52
Design Enhancement: Learn how you modify your design to
enhance the performance

Many applications processes the entries in order, i.e., moving from one
entry to the next.

Many other applications refer to the same entry many times.

Then, our current linked implementation is very inefficient, since it


moves from the head to the element every time!
Then, we need to remember the last position currentpos and start
navigating from it, and we use current to start walking from
currentpos

k 0 1 k size-1
currentpos current size head
© Waleed A. Yousef 2008 53
Design Enhancement: Learn how you modify your design to
enhance the performance

Of course, this will not help if the new element is preceding


the last element visited.

Only the type definition, InsertList, DeleteList,


ReplaceList, and RetrieveList will change.

typedef struct list{


ListNode *head, *current;
int size, currentpos;
}List;

k 0 1 k size-1
currentpos current size head
© Waleed A. Yousef 2008 54
void InsertList(int pos, ListEntry e, List *pl){
ListNode *p; 0
p=(ListNode *)malloc(sizeof(ListNode));
p->entry=e;

cu pos

t
en
p->next=NULL;

t
rr
en
if (pos==0){ currentpos

rr
p->next=pl->head;

cu
pl->head=p;
pl->currentpos=0; //new pos-1
pl->current=pl->head; //new pl->current
}
else{//pl->current is used in place of q previously.
if (pos<=pl->currentpos){ pos
pl->currentpos=0;//as i=0
pl->current=pl->head;//as q=pl->head
} //new.


for(; pl->currentpos!=pos-1; pl->currentpos++)
pl->current=pl->current->next; p
p->next=pl->current->next;
pl->current->next=p; size-1
}
pl->size++;
© Waleed A. Yousef 2008 55
void DeleteList(int pos, ListEntry *pe, List *pl){
ListNode *tmp; 0
if (pos==0){
*pe=pl->head->entry;

cu pos

t
en
pl->current=pl->head->next;

t
rr
en
free(pl->head); currentpos

rr
pl->head=pl->current; //new

cu
pl->currentpos=0; //new
} pos-1
else{ pl->current
if (pos<=pl->currentpos){
pl->currentpos=0;
pl->current=pl->head; pos
tmp
}
for(; pl->currentpos!=pos-1; pl->currentpos++)
pl->current=pl->current->next;


*pe=pl->current->next->entry;
tmp=pl->current->next->next;
free(pl->current->next);
pl->current->next=tmp; size-1
}
pl->size--;
© Waleed A. Yousef 2008 56
You have to modify ReplaceList and RetrieveList in
the same manner. (Do it as a homework). Check also for other
functions, e.g., CreateList (for initialization)

Compare the previous functions to the functions of the book,


where it has a function SetPosition that is called from
within InsertList and DeleteList.

Having this function may simplify the code for the case of
having current and currentpos.

© Waleed A. Yousef 2008 57


Design Enhancement: Learn how you modify your design to
enhance the performance

Accessing the list at a position preceding currentpos will


be slow, since we cannot move back. A possible remedy is
using doubly linked list.

We need just a pointer, current, not necessarily point to


the first node. currentpos will always indicates the order
of the current node.

Read the code from the book and solve the review problems

k 0 1 k size-1
currentpos current size
© Waleed A. Yousef 2008 58

You might also like