0% found this document useful (0 votes)
30 views28 pages

Unit Ii - Stack & Queues

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)
30 views28 pages

Unit Ii - Stack & Queues

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/ 28

DATA STRUCTURE AND ALGORITHMS

(23UCS02)

LECTURE NOTES

UNIT - II

B.Sc (CS) I YEAR

II SEMESTER

Dr R U ANITHA, Assistant Professor


Mrs M THENMOZHI, Assistant Professor

DEPARTMENT OF COMPUTER SCIENCE


SONA COLLEGE OF ARTS AND SCIENCE, SALEM
Unit – II STACK AND QUEUE
Unit – II
Stack ADT – Operations – Applications – Evaluating arithmetic expressions –
Conversion of infix to post fix expression – Queue ADT – operations – Circular
Queue – Priority Queue – deQueue applications of queue.
2.1 STACK
A stack is an Abstract Data Type (ADT), commonly used in most
programming languages. It is named stack as it behaves like a real-world stack, for
example – a deck of cards or a pile of plates, etc.

A real-world stack allows operations at one end only. For example, it can
place or remove a card or plate from the top of the stack only. Likewise, Stack ADT
allows all data operations at one end only. At any given time, it can only access the
top element of a stack.
This feature makes it LIFO data structure. LIFO stands for Last-in-first-out.
Here, the element which is placed (inserted or added) last, is accessed first. In stack
terminology, insertion operation is called PUSH operation and removal operation is
called POP operation.

2.1 Stack Representation


The following diagram depicts a stack and its operations.

1
A stack can be implemented by means of Array, Structure, Pointer, and
Linked List. Stack can either be a fixed size one or it may have a sense of dynamic
resizing. Here, it is going to implement stack using arrays, which makes it a fixed
size stack implementation.

2.1.1 Basic Operations


Stack operations may involve initializing the stack, using it and then de-
initializing it. Apart from these basic stuffs, a stack is used for the following two
primary operations.
1. push() − Pushing (storing) an element on the stack.
2. pop() − Removing (accessing) an element from the stack.

To use a stack efficiently, it need to check the status of stack as well. For the
same purpose, the following functionality is added to stacks.
3. peek() − get the top data element of the stack, without removing it.
4. isFull() − check if stack is full.
5. isEmpty() − check if stack is empty.
At all times, it maintain a pointer to the last PUSHed data on the stack. This
pointer always represents the top of the stack, hence named top. The top pointer
provides top value of the stack without actually removing it.

1.Push Operation
The process of putting a new data element onto stack is known as a Push
Operation. Push operation involves a series of steps
Step 1 − Checks if the stack is full.
Step 2 − If the stack is full, produces an error and exit.
Step 3 − If the stack is not full, increments top to point next empty space.
Step 4 − Adds data element to the stack location, where top is pointing.
Step 5 − Returns success.

2
If the linked list is used to implement the stack, then in step 3, it need to allocate
space dynamically.

Algorithm for PUSH Operation


begin procedure push: stack, data
if stack is full
return null
endif
top ← top + 1
stack[top] ← data
end procedure

Implementation of this algorithm in C, is very easy. See the following code


Example

void push(int data) {


if(!isFull()) {
top = top + 1;
stack[top] = data;
} else {
printf("Could not insert data, Stack is full.\n");
}}

2. pop operation
Accessing the content while removing it from the stack, is known as a Pop
Operation. In an array implementation of pop() operation, the data element is not
actually removed, instead top is decremented to a lower position in the stack to point
to the next value. But in linked-list implementation, pop() actually removes data
element and deallocates memory space.
A Pop operation may involve the following steps.
Step 1 − Checks if the stack is empty.

3
Step 2 − If the stack is empty, produces an error and exit.
Step 3 − If the stack is not empty, accesses the data element at which top is
pointing.
Step 4 − Decreases the value of top by 1.
Step 5 − Returns success.

Algorithm for Pop Operation


A simple algorithm for Pop operation can be derived as follows −

begin procedure pop: stack


if stack is empty
return null
endif
data ← stack[top]
top ← top - 1
return data
end procedure

Implementation of this algorithm in C, is as follows −


Example

int pop(int data) {


if(!isempty()) {
data = stack[top];
top = top - 1;
return data;
} else {
printf("Could not retrieve data, Stack is empty.\n");
}}

4
3. peek()
Algorithm of peek() function
begin procedure peek
return stack[top]
end procedure
Implementation of peek() function in C programming language
Example

int peek() { return stack[top];}

4. isfull()
Algorithm of isfull() function

begin procedure isfull


if top equals to MAXSIZE
return true
else
return false
endif
end procedure

Implementation of isfull() function in C programming language.


Example

bool isfull() {
if(top == MAXSIZE)
return true;
else
return false; }

5. isempty()
Algorithm of isempty() function.

begin procedure isempty


if top less than 1
return true
else

5
return false
endif
end procedure

Implementation of isempty() function in C programming language is slightly


different. It initialize top at -1, as the index in array starts from 0. So it check if the
top is below zero or -1 to determine if the stack is empty.
Example

bool isempty() {
if(top == -1)
return true;
else
return false;
}

2.2 Applications of Stack

The following are the applications of the stack:

1. Balancing of symbols: Stack is used for balancing a symbol. For example, in the
following program:

int main()
{
cout<<"Hello";
cout<<"javaTpoint";
}

Each program has an opening and closing braces; when the opening braces
come, it push the braces in a stack, and when the closing braces appear, it pop the
opening braces from the stack. Therefore, the net value comes out to be zero. If any
symbol is left in the stack, it means that some syntax occurs in a program.

2. String reversal: Stack is also used for reversing a string. For example, it needs to
reverse a "javaTpoint" string, so it can achieve this with the help of a stack.
First, it push all the characters of the string in a stack until it reach the null character.

6
After pushing all the characters, it starts taking out the character one by one until i
reach the bottom of the stack.

3. UNDO/REDO: It can also be used for performing UNDO/REDO operations. For


example, it have an editor in which it write 'a', then 'b', and then 'c'; therefore, the text
written in an editor is abc. So, there are three states, a, ab, and abc, which are stored in
a stack. There would be two stacks in which one stack shows UNDO state, and the
other shows REDO state.
If it want to perform UNDO operation, and want to achieve 'ab' state, then implement
pop operation.

4. Recursion: The recursion means that the function is calling itself again. To
maintain the previous states, the compiler creates a system stack in which all the
previous records of the function are maintained.

5. DFS(Depth First Search): This search is implemented on a Graph, and Graph uses
the stack data structure.

6. Backtracking: Suppose it have to create a path to solve a maze problem. If it is


moving in a particular path, and realize that it comes on the wrong way. In order to
come at the beginning of the path to create a new path, it has to use the stack data
structure.

7. Expression conversion: Stack can also be used for expression conversion. This is
one of the most important applications of stack. The list of the expression conversion
is given below:

o Infix to prefix
o Infix to postfix
o Prefix to infix
o Prefix to postfix
Postfix to infix
8. Memory management: The stack manages the memory. The memory is assigned
in the contiguous memory blocks. The memory is known as stack memory as all the
variables are assigned in a function call stack memory. The memory size assigned to
the program is known to the compiler. When the function is created, all its variables
are assigned in the stack memory. When the function completed its execution, all the
variables assigned in the stack are released.

7
2.2 QUEUE
Queue is an abstract data structure, somewhat similar to Stacks. Unlike stacks,
a queue is open at both its ends. One end is always used to insert data (enqueue) and
the other is used to remove data (dequeue). Queue follows First-In-First-Out
methodology, i.e., the data item stored first will be accessed first.

A real-world example of queue can be a single-lane one-way road, where the


vehicle enters first, exits first. More real-world examples can be seen as queues at the
ticket windows and bus-stops.

2.1 Queue Representation


Queue can access both ends for different reasons. The following diagram
given below tries to explain queue representation as data structure.

As in stacks, a queue can also be implemented using Arrays, Linked-lists,


Pointers and Structures. For the sake of simplicity, it shall implement queues
using one-dimensional array.

2.1.1 Basic Operations


Queue operations may involve initializing or defining the queue, utilizing it,
and then completely erasing it from the memory. The basic operations associated
with queues are as follows
1. enqueue() − add (store) an item to the queue.
2. dequeue() − remove (access) an item from the queue.

Few more functions are required to make the above-mentioned queue


operation efficient. These are
3. peek() − Gets the element at the front of the queue without removing it.

8
4. isfull() − Checks if the queue is full.

5. isempty() − Checks if the queue is empty.

In queue, dequeue (or access) data pointed by front pointer and enqueing (or
storing) data in the queue take help of rear pointer.

1. enqueue Operation
Queues maintain two data pointers, front and rear. Therefore, its operations
are comparatively difficult to implement than that of stacks.
The following steps should be taken to enqueue (insert) data into a queue
Step 1 − Check if the queue is full.
Step 2 − If the queue is full, produce overflow error and exit.
Step 3 − If the queue is not full, increment rear pointer to point the next empty
space.
Step 4 − Add data element to the queue location, where the rear is pointing.
Step 5 − return success.

Sometimes, check to see if a queue is initialized or not, to handle any


unforeseen situations.

Algorithm for enqueue operation


procedure enqueue(data)
if queue is full
return overflow
endif
rear ← rear + 1
queue[rear] ← data
return true
end procedure

9
Implementation of enqueue() in C programming language
Example

int enqueue(int data)


if(isfull())
return 0;
rear = rear + 1;
queue[rear] = data;
return 1;
end procedure

2. Dequeue Operation
Accessing data from the queue is a process of two tasks − access the data
where front is pointing and remove the data after access. The following steps are
taken to perform dequeue operation.
Step 1 − Check if the queue is empty.
Step 2 − If the queue is empty, produce underflow error and exit.
Step 3 − If the queue is not empty, access the data where front is pointing.
Step 4 − Increment front pointer to point to the next available data element.
Step 5 − Return success.

10
Algorithm for dequeue operation
procedure dequeue
if queue is empty
return underflow
end if
data = queue[front]
front ← front + 1
return true
end procedure

Implementation of dequeue() in C programming language


Example

int dequeue() {
if(isempty())
return 0;
int data = queue[front];
front = front + 1;
return data;
}

3. peek()
This function helps to see the data at the front of the queue. The algorithm of
peek() function is as follows.
Algorithm
begin procedure peek
return queue[front]
end procedure
Implementation of peek() function in C programming language
Example

int peek() {
return queue[front]; }

11
4. isfull()
As it using single dimension array to implement queue, it just check for the
rear pointer to reach at MAXSIZE to determine that the queue is full. In case it
maintain the queue in a circular linked-list, the algorithm will differ.

Algorithm isfull() function


begin procedure isfull
if rear equals to MAXSIZE
return true
else
return false
endif
end procedure

Implementation of isfull() function in C programming language


Example

bool isfull() {
if(rear == MAXSIZE - 1)
return true;
else
return false; }

5. isempty()
Algorithm of isempty() function

begin procedure isempty


if front is less than MIN OR front is greater than rear
return true
else
return false
endif
end procedure

If the value of front is less than MIN or 0, it tells that the queue is not yet
initialized, hence empty.

12
Example

bool isempty() {
if(front < 0 || front > rear)
return true;
else
return false;
}

2.2 Applications of Queue

Due to the fact that queue performs actions on first in first out basis which is
quite fair for the ordering of actions. There are various applications of queues
discussed as below. Queues are widely used as waiting lists for a single shared
resource like printer, disk, CPU.

1.Queues are used in asynchronous transfer of data (where data is not being
transferred at the same rate between two processes) for eg. pipes, file IO, sockets.
2.Queues are used as buffers in most of the applications like MP3 media player, CD
player, etc.
3.Queues are used to maintain the play list in media players in order to add and
remove the songs from the play-list.
4.Queues are used in operating systems for handling interrupts.

3. CIRCULAR QUEUE

In Circular Queue, the insertion of a new element is performed at the very first
location of the queue if the last location of the queue is full, in which the first element
comes just after the last element.

 A circular queue is an abstract data type that contains a collection of data


which allows addition of data at the end of the queue and removal of data at
the beginning of the queue.
 Circular queues have a fixed size.
 Circular queue follows FIFO principle.
 Queue items are added at the rear end and the items are deleted at front end of
the circular queue

13
 Here the Queue space is utilized fullyby inserting the element at the Front end
if the rear end is full.

Operations on Circular Queue

Fundamental operations performed on the Circular Queue are

 Circular Queue Enqueue


 Circular Queue Dequeue

Formula to be used in Circular Queue

For Enqueue

Rear = ( Rear + 1) % ArraySize

For Dequeue

Front = ( Front + 1) % ArraySize

(i) Circular Queue Enqueue Operation

 It is same as Linear Queue EnQueue Operation (i.e) Inserting the element at


the Rear end. First check for full Queue.
 If the circular queue is full, then insertion is not possible.
 Otherwise check for the rear end.
 If the Rear end is full, the elements start getting inserted from the Front end.

14
Routine to Enqueue an element in circular queue

void Enqueue ( int X, CircularQueue CQ )


{
if( Front = = ( Rear + 1 ) % ArraySize)
Error( “Queue is
full!!Insertion not
possible” );
else if( Rear = = -1 )
{
Front = Front + 1;
Rear = Rear + 1;
CQ[ Rear ] = X;
}
else
{
Rear = ( Rear + 1 ) % Arraysize;
CQ[ Rear ] = X;
}
}

Circular Queue DeQueue Operation

 It is same as Linear Queue DeQueue operation (i.e) deleting the front element.
First check for Empty Queue.
 If the Circular Queue is empty, then deletion is not possible.

15
 If the Circular Queue has only one element, then the element is deleted and
Front and Rear pointer is initialized to - 1 to represent Empty Queue.
 Otherwise, Front element is deleted and the Front pointer is made to point to
next element in the Circular Queue.

F, R
F= -1,R= --1

Routine To DeQueue An Element In Circular Queue


void DeQueue (CircularQueue CQ)
{
if(Front== - 1) Empty(“Empty Queue!”);
else if(Front==rear)
{
X=CQ[Front]; Front=-1; Rear=-1;
}
else
{
X=CQ[Front]; Front=(Front+1)%Arraysize;
}}

DOUBLE-ENDED QUEUE (DEQUE)


In DEQUE, insertion and deletion operations are performed at both ends of the
Queue.

16
Exceptional Condition of DEQUE

i) Input Restricted DEQUE

Here insertion is allowed at one end and deletion is allowed at both ends.

Deletion
Insertion
Front Rear

Deletion

ii) Output Restricted DEQUE

Here insertion is allowed at both ends and deletion is allowed at one end.

Insertion
Deletion Insertion

Front Rear

Operations on DEQUE
Four cases for inserting and deleting the elements in DEQUE are

1. Insertion At Rear End [ same as Linear Queue ]


2. Insertion At Front End
3. Deletion At Front End [ same as Linear Queue ]
4. Deletion At Rear End

17
Case 1: Routine to insert an element at Rear end
void Insert_Rear (int X, DEQUE DQ)
{
if( Rear = = Arraysize - 1)
Error(“Full
Queue!!!!
Insertion not
possible”);else if(
Rear = = -1)
{
Front = Front +
1; Rear = Rear +
1; DQ[ Rear ] =
X;
}

else
{
Rear = Rear +
1; DQ[ Rear ] =
X;
}
}

18
19
Case 2: Routine to insert an element at Front end
void Insert_Front ( int X, DEQUE DQ )
{
if( Front = = 0 )
Error(“Element present in Front!!!!! Insertion not possible”);
else if(Front = = -1)
{
Front = Front + 1; Rear = Rear + 1; DQ[Front] = X; }
else
{ Front = Front - 1;
DQ[Front] = X; } }

Case 3: Routine to delete an element from Front end


void Delete_Front(DEQUE DQ)
{ if(Front = = - 1)
Error(“Empty queue!!!!
Deletion not possible”);else if(
Front = = Rear )
{ X = DQ[ Front]; Front = - 1; Rear = - 1;
}
Else
{ X = DQ [ Front];
Front = Front + 1; } }

20
Case 4: Routine to delete an element from Rear end

void Delete_Rear(DEQUE DQ)


{
if( Rear = = - 1)
Error(“Empty queue!!!! Deletion not possible”);
else if( Front = = Rear )
{
X = DQ[ Rear ];
Front = - 1; Rear = - 1; }
else
{ X = DQ[ Rear ];
Rear = Rear - 1; } }

21
2.3 EVALUATION OF ARITHMETIC EXPRESSIONS
 An arithmetic expression consists of both operands and operators.
 Operands are variables or constants and operators are of various types like arithmetic,
comparison etc.
 The problem to evaluate an arithmetic expression is the order of evaluation.
There are two ways to fix it:
1. Precedence and associativity can be assigned to each operator.
2. Expression can be fully parenthesized.
 The innermost parenthesis part is called sub-expression.

22
 Another problem in the evaluation of arithmetic expression is that how compiler can
generate code for agiven expression.
This problem can be resolved in two steps :
1. Conversion of a given expression into a special notation.
2. Evaluation/production of object code using stack.

Precedence and Associativity of Usual Operators


OPERATORS PRECEDENCE ASSOCIATIVITY
- (unary), +(unary), NOT 6 -
^ (Exponentiation) 6 Right to Left
*(multiplication), / (division) 5 Left to Right
+(addition), - (Subtraction) 4 Left to Right
<,>,<=,>= 3 Left to Right
AND 2 Left to Right
OR, XOR 1 Left to Right

Example
A simple arithmetic expression is cited below:

Another way of fixing the order of evaluation is parenthesize the expression fully.
(this allowsone to override the rule for precedence and associativity)

Example
A simple arithmetic expression is cited below:

23
Notations for Arithmetic Expression
The way to write arithmetic expression is known as a notation. An arithmetic
expression can be written in three different but equivalent notations, i.e., without changing
the essence or output of an expression. These notations are.

1. Infix Notation
2. Prefix (Polish) Notation
3. Postfix (Reverse-Polish) Notation
These notations are named as how they use operator in expression.

1. Infix Notation
The infix notation is a - b + c, where operators are used in-between operands. It is easy for
us humans to read, write, and speak in infix notation but the same does not go well with
computing devices. An algorithm to process infix notation could be difficult and costly in terms
of time and space consumption.

2. Prefix Notation
In this notation, operator is prefixed to operands, i.e. operator is written ahead of
operands. For example, +ab. This is equivalent to its infix notation a + b. Prefix notation is also
known as Polish Notation.

3. Postfix Notation
This notation style is known as Reversed Polish Notation. In this notation style, the
operator is postfixed to the operands i.e., the operator is written after the operands. For
example, ab+. This is equivalent to its infix notation a + b.
The following table briefly tries to show the difference in all three notations
Sr.No. Infix Notation Prefix Notation Postfix Notation
1 a+b +ab ab+
2 (a + b) ∗ c ∗+abc ab+c∗
3 a ∗ (b + c) ∗a+bc abc+∗
4 a/b+c/d +/ab/cd ab/cd/+
5 (a + b) ∗ (c + d) ∗+ab+cd ab+cd+∗
6 ((a + b) ∗ c) - d -∗+abcd ab+c∗d-

24
Parsing Expressions
It is not a very efficient way to design an algorithm or program to parse infix notations.
Instead, these infix notations are first converted into either postfix or prefix notations and then
computed. To parse any arithmetic expression, it need to take care of operator precedence and
associativity also.

Precedence
When the operand is in between two different operators, then operator will take the
operand first, is decided by the precedence of an operator over others. For example,
multiplication operation has precedence over addition, b * c will be evaluated first. A table of
operator precedence is provided later.

Associativity
Associativity describes the rule where operators with the same precedence appear in an
expression. For example, in expression a + b − c, both + and – have the same precedence, then
which part of the expression will be evaluated first, is determined by associativity of those
operators. Here, both + and − are left associative, so the expression will be evaluated as (a + b)
− c.
Precedence and associativity determines the order of evaluation of an expression.
Following is an operator precedence and associativity table (highest to lowest)
Sr.No. Operator Precedence Associativity
1 Exponentiation ^ Highest Right Associative
2 Multiplication ( ∗ ) & Second Highest Left Associative
3 Addition
Division( +
( /))& Lowest Left Associative
Subtraction ( − )
The above table shows the default behavior of operators. At any point of time in
expression evaluation, the order can be altered by using parenthesis. For example, In a + b*c,
the expression part b*c will be evaluated first, with multiplication as precedence over addition.
It use parenthesis for a + b to be evaluated first, like (a + b)*c.
Postfix Evaluation Algorithm
The algorithm flows on how to evaluate postfix notation.
Step 1 − scan the expression from left to right
Step 2 − if it is an operand push it to stack
Step 3 − if it is an operator pull operand from stack and perform operation
Step 4 − store the output of step 3, back to stack
Step 5 − scan the expression until all operands are consumed
Step 6 − pop the stack and perform operation
25
Infix to Postfix Conversion
Let Q be an arithmetic expression written in infix notation. Let P be an equivalent postfix
notation generated by infix to postfix conversion algorithm. Q may contain ( , ) , ^ , * , / , +
and – .
Algorithm
1) Scan the infix string from left to right.
2) Initialize an empty stack
3) If the character is an operand, Then Add it to the postfix string
4) If the current character is an operator and if the stack is empty Then
5) Push the character to the stack,
6) If the current character is an operator and the stack is not empty Then
i) Compare the precedence of the character with the operator on top of the
stack.
ii) While operator at top of the stack has higher precedence over the
current character and stack isnot empty,
a. Pop the stack
b. Add the popped character to the postfix string
iii) Push the scanned character to stack.
7) Repeat steps 3-5 till all the characters are scanned
8) While stack is not empty
i) Add operator in top of stack to postfix string
ii) Pop the stack
9) Return the postfix string
Example
 Consider the infix expression Q = A + B ^ ( D / E )
 The postfix conversion steps are tabulated below :

Element Scanned Stack Operation Expression (P)


A ( Add to P A
+ ( + Push + A
B ( + Add to P A B
^ ( + ^ Push ^ A B
( ( + ^ ( Push ( A B
D ( + ^ ( Add to P A B D
/ ( + ^ ( / Push / A B D
E ( + ^ ( / Add to P A B D E
) ( + ^ Pop /, ( A B D E /
) Pop ^, + A B D E / ^ +

26
Evaluation of Postfix Expression
Suppose ‘P’ is an arithmetic expression written in postfix notation, the following
algorithm, which usesa STACK to hold operands, evaluates ‘P’.

Steps

1. Add a right parenthesis “)” at the end of ‘P’.


2. Scan P from L to R and repeat steps 3 and 4 for each element of ‘P’ until the “)”
is encountered.
3. If an operand is encountered, push it onto the STACK.
4. If an operator is found, then
a. Pop the 2 elements of STACK, where ‘A’ is the top element and ‘B’ is
the next-to-top element.
b. Evaluate B operator A
c. Place the result of (B) onto STACK.
5. Set VALUE equal to the top element on STACK.
6. EXIT.

Example
Consider the following arithmetic expression P written in postfix notation:
P: 5, 6, 2, +, *, 12, 4, /, - )

SYMBOL SCANNED STACK


5 5
6 5, 6
2 5, 6, 2
+ 5, 8 6 +2
* 40 5*8
12 40, 12
4 40, 12, 4
/ 40, 3 12/4
- 37 40-3
)

27

You might also like