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

Ds - Unit 3 - Stack

The document discusses stacks, which are linear data structures that follow the LIFO (last-in, first-out) principle. Elements can only be inserted or removed from one end, called the top. Stacks have two main operations: push to insert and pop to remove elements from the top. Stacks can be represented using arrays or linked lists. Array representations use a TOP pointer and check for overflow on push. Linked lists avoid capacity limits and overflow checks. The document also covers infix, postfix, and prefix notation for arithmetic expressions and provides algorithms to evaluate expressions in postfix form using a stack.

Uploaded by

farooq886702
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
36 views

Ds - Unit 3 - Stack

The document discusses stacks, which are linear data structures that follow the LIFO (last-in, first-out) principle. Elements can only be inserted or removed from one end, called the top. Stacks have two main operations: push to insert and pop to remove elements from the top. Stacks can be represented using arrays or linked lists. Array representations use a TOP pointer and check for overflow on push. Linked lists avoid capacity limits and overflow checks. The document also covers infix, postfix, and prefix notation for arithmetic expressions and provides algorithms to evaluate expressions in postfix form using a stack.

Uploaded by

farooq886702
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 17

Unit 3 Chapter 1

Stacks
Introduction
Stack is a linear structure in which items may be added or removed only at one end. Eg. A stack of
dishes, a stack of building blocks etc., Observe that an item may be added or removed only from the
top of any of the stacks. This means that in particular, the last item added to a stack is the first item to
be removed. Stacks are also called Last-In-First-Out (LIFO) lists.
A stack can be referred as a list of elements in which an element may be inserted or deleted only at
one end, called the top of the stack. This means in particular, that elements are removed from a stack
in the reverse order of that in which they were inserted into the stack.
The two basic operations associated with stacks are:
Push : used to insert an element into a stack
Pop : used to delete or remove an element from a stack.

Example: Suppose we need to push the following elements in the stack:


AAA, BBB, CCC, DDD, EEE, FFF
The diagram given here shows three different ways of picturing such a stack.

The implication is that the right-most element is the top element. Regardless of the way a stack is
described, its underlying property is that insertions and deletions can occur only at the top of the
stack. This means EEE cannot be deleted before FFF is deleted. Similarly, DDD cannot be deleted
before EEE is deleted. The same pattern for all other elements also. Consequently, the elements may
be popped from the stack only in the reverse order of that in which they were pushed onto the stack.
Array representation of Stacks
Stacks may be represented in computer usually by means of one-way list or a linear array STACK,
unless otherwise stated or implied. A pointer variable TOP, which contains the location of the top
element of the stack and a variable MAXSTACK which gives the maximum number of elements that
can be held by the stack are used. The condition TOP = 0 or TOP = NULL indicates that the stack is
empty.
The diagram given below shows an array representation of a stack. Since TOP = 3, the stack has three
elements XXX, YYY, ZZZ and since MAXSTACK = 8, still 5 more elements can be pushed into the stack.

The operations of adding (pushing) an item onto a stack and the operation of removing (popping) an
item from a stack may be implemented respectively by the procedures – PUSH and POP. During a
PUSH, we must first test whether there is room in the stack for the new item. If not, then we have the
condition known as overflow. Similarly, during a POP, we must initially test whether there is an
element in the stack to be deleted. If not, then we have the condition known as underflow.
Algorithm for PUSH operation
PUSH (STACK, TOP, MAXSTK, ITEM)
1. [Stack is already filled?]
IF TOP == MAXSTACK-1, then print the message “OVERFLOW” and
return.
2. Set TOP = TOP + 1 (Increase Top by 1 )
3. Set STACK [TOP] = ITEM (Insert the item in new TOP position).
4. Return.
Algorithm for POP operation
POP (STACK, TOP, ITEM)
1. [Stack has an item to be removed?]
2. If TOP == -1 then print the message “Underflow” and return.
3. Set ITEM = STACK[TOP] (assigns TOP element to ITEM)
4. Set TOP = TOP – 1 (Decreases TOP by 1)
5. Return.
Note: value of TOP is changed before the insertion in PUSH and it is changed after deletion in POP.
Note:

Minimizing overflow
There is an essential difference between underflow and overflow while dealing with stack. Underflow
depends exclusively upon the given algorithm and the given input of data and hence there is no direct
control by the programmer. Overflow, on the other hand, depends upon the arbitrary choice of the
programmer for the amount of memory space reserved for each stack and this choice does influence
the number of times overflow may occur.
The number of elements in a stack fluctuates as elements are added to or removed from the stack.
Initially reserving a great deal of space for each stack will decrease the number of times overflow may
occur; however this result in expensive use of the space if most of the space is used occasionally. On
the other hand, reserving a small amount of space for each stack may result in increase in number of
occurrence of overflow and the time required for resolving an overflow (such as adding the space to
the stack) may be more expensive than the space saved.
Nowadays, various techniques have been developed which modify the array representation of stacks
so that the amount of space reserved for more than one stack is used more efficiently.

LINKED LIST REPRESENTATION OF STACKS


The linked representation of a stack, commonly termed linked stack. This is usually implemented using
a singly linked list. The INFO fields of the nodes hold the elements of the stack and the LINK fields hold
the pointers to the neighboring element in the stack. The START pointer of the linked list behaves as
the TOP pointer variable of the stack and the null pointer of the last node in the list, which indicates
the bottom of the stack. The following diagram shows the pictorial representation.

The PUSH operation into STACK is accomplished by inserting a node into the front or start of the list
and a POP operation is undertaken by deleting the node pointed to by the STACK pointer. Figures
given below shows the PUSH and POP operation on the linked stack.
Array representation of stack needs the variable MAXSTK which gives the maximum number of
elements that can be held by the stack. Also, it calls for the checking of OVERFLOW in the case of
PUSH operation (TOP == MAXSTACK-1) and UNDERFLOW in case of POP operation (TOP == -1).
Linked representation of stack is free of these requirements. There is no limitation on the capacity of
the linked stack and hence it can support as many PUSH operations as the free storage list can support.
This avoids the need to maintain the MAXSTK variable and consequently on the checking of
OVERFLOW during a PUSH operation. Diagram given below shows the algorithm for PUSH and POP
operation. (You may refer notes).
However, the condition TOP=NULL may be retained in the POP procedure to prevent deletion from an
empty linked stack and the condition AVAIL=NULL to check for available space in the free-storage list.
(i) Algorithm for inserting an item into the stack (PUSH)using linked list

(ii) Algorithm for deleting an item from the stack (POP) using linked list

ARITHMETIC EXPRESSIONS: POLISH NOTATION


Let Q be an arithmetic expression involving constants and operations. Binary operations in Q may have different
levels of precedence. Specifically, we assume the following three levels of precedence for usual binary operations:
Highest : Exponentiation (↑)
Next Highest : Multiplication (*) and Division (/)
Lowest : Addition (+) and Subtraction ( - )
For simplicity, we assume that Q contains no unary operation (e.g. a leading minus sign).
Infix Notation
For most common arithmetic operations, the operator symbol is placed between its two operands. Eg,
A+B C–D E*F G/H
This is called infix notation. With this notation, we must distinguish between
(A+B)*C and A + (B*C)
by using either parenthesis or some operator precedence levels. Accordingly, the order of the operators and
operands in an arithmetic expression does not uniquely determine the order in which the operations are to be
performed.
Polish Notation, named after the Polish mathematician Jan Lukasiewicz, refers to the notation in which the
operator symbol is placed before its two operands. For example,
+AB -CD *EF /’GH
A step by step translation from infix expression to polish notation is given below: (Square brackets are used to
indicate partial translation):
(A+B)*C = [+AB]*C = *+ABC
A+(B*C) = A+[*BC] = +A*BC
(A+B)/(C-D) = [+AB]/[-CD] = /+AB-CD
The fundamental property of Polish notation is that the order in which the operations are to be performed is
completely determined by the positions of the operators and operands in the expression. One never needs
parentheses when writing expressions in Polish Notation.
Reverse Polish Notation refers to the analogous notation in which the operator symbol is placed after its two
operands:
AB+ CD- EF* GH/
One never needs parentheses to determine the order of the operations in any arithmetic expression written in
Reverse Polish notation. This notation is frequently called postfix (or suffix) notation, whereas prefix notation is
the term used for Polish notation.
Computer evaluates an arithmetic expression written in infix notation in the following two steps – It converts the
given expression to postfix notation and then it evaluates the postfix expression. In each step, the stack is the main
tool that is used to accomplish the given task.
Evaluation of a Postfix expression
Suppose P is an arithmetic expression written in postfix notation. The following algorithm, which uses a STACK to
hold operands, evaluates P
Algorithm: to find the VALUE of an arithmetic expression P written in postfix notation
1. Add a right parenthesis ) at the end of P [ this acts as a sentinel ]
2. Scan P from left to right and repeat Steps 3 and 4 for each element of P until the sentinel ) is encountered.
3. If an operand is encountered, put it on STACK
4. If an operator  is encountered, then
a. Remove the two top elements of STACK, where A is the top element and B is the next-to-top
element
b. Evaluate B  A
c. Place the result of (b) back on STACK
[End of if structure]
[End of step 2 loop]
5. Set VALUE equal to the top element on STACK
6. EXIT
Here is an example for the same.

Example 2
Assume the following is the Postfix expression
53+62/*35 *+
The expression is evaluated as follows:
Steps :
1. Read every value (character) in the given expression
2. If it an operand then push it into the stack
3. If it is an operator pop the top two elements from the stack and store them in two different variables.
4. Perform required operation, based on the operator and store the result in a variable (eg. res)
5. Push the result in to the stack
6. Continue the above said operations till the entire expression is evaluated
Initially as 5 is an operand, it is pushed into the stack at the 0th position. 0 5
Then the 2nd value 3 is pushed into the stack as it is an operand. 1 3
0 5
Now the third character “+” is read. It is an operator.
So, the top 2 values from the stack are popped out and stored in two variables A and B
i.e. A = 3 and B = 5
Using the operator +, perform the operation (i.e. addition) and store the result in a variable (eg. res). And push it
into the stack. ( B + A = 5 + 3 = 8. Now res is 8.). Now the stack looks like 0 8
Continue reading the next value. i.e. 6. As it is an operand, push it into the stack.
1 6
Next value is 2. As it an operand, push it into the stack. 2 2 0 8
1 6
0 8
Next value is an operator. i.e. / (division).
So, top 2 values from the stack are popped out and stored in two variables A and B. i.e. A = 2 and B = 6
Using the operator /, perform the operation (i.e. division) and store the result in a variable (eg. res). And push it
into the stack. ( B / A = 6 / 2 = 3. Now res is 3.). Now the stack looks like 1 3
0 8
Now the next value is an operator. i.e. * (Multiplication)
So, top 2 values from the stack are popped out and stored in two variables A and B i.e. A = 3 and B = 8
Using the operator *, perform the operation (i.e. multiplication) and store the result in a variable (eg. res). And
push it into the stack. ( B * A = 8 * 3 = 24. Now res is 24.). Now the stack looks like
0 24
Continue reading the next value. i.e. 3. As it is an operand, push it into the stack
1 3
2 5 0 24
Next value is 5. As it an operand, push it into the stack. 1 3
0 24
Now the next value is an operator. i.e. * (Multiplication)
So, top 2 values from the stack are popped out and stored in two variables A and B. i.e. A = 5 and B = 3
Using the operator *, perform the operation (i.e. multiplication) and store the result in a variable (eg. res). And
push it into the stack. ( B * A = 3 * 5 = 15. Now res is 15.). Now the stack looks like 1 15
Now the next value is an operator. i.e. + (Addition) 0 24

So, the top 2 values from the stack are popped out and stored in two variables A and B
i.e. A = 15 and B = 24
Using the operator *, perform the operation (i.e. addition) and store the result in a variable (eg. res). And push it
into the stack. ( B + A = 24 * 15 = 39. Now res is 15.). Now the stack looks like
0 39
Now, we reached the end of the expression. Show the result which is in the stack as the output.

Transforming Infix Expressions into Postfix Expressions


Let Q be an arithmetic expression written in infix notation. Besides operands and operators, Q may also contain
left and right parentheses. We assume that the operators in Q consist only of exponentiations (↑), multiplications
(*), divisions (/), additions (+) and subtractions (-) and that they have the usual three levels of precedence. We also
assume that operators on the same level, including exponentiations are performed from left to right unless
otherwise indicated by parentheses.
Note : Precedence and associativity of Operators.
(associativity is a property that determines how operators of the same precedence are grouped in the absence of parentheses)

1. Parenthesis
2. Exponent Right to Left
3. Divide and Multiply Left to Right
4. Addition and Subtraction Left to Right
Following algorithm transforms the infix expression Q into its equivalent postfix expression P. It uses a stack to
temporarily hold operators and left parentheses. The postfix expression P will be constructed from left to right
using the operands from Q and the operators which are removed from STACK. It begins by pushing a left
parenthesis onto the stack and adding a right parenthesis at the end of Q and is completed when STACK is empty.
Input: x^y/(5*z)+2 Output: xy^5z*/2+

Algorithm to convert Infix To Postfix

Let, X is an arithmetic expression written in infix notation. This algorithm finds the equivalent postfix
expression P.
1. Push “(“ onto Stack, and add “)” to the end of X.
2. Scan X from left to right and repeat Step 3 to 6 for each element of X until the Stack is empty.
3. If an operand is encountered, add it to P.
4. If a left parenthesis is encountered, push it onto Stack.
5. If an operator is encountered ,then:
a. Repeatedly pop from Stack and add to P each operator (on the top of Stack) which has the same
precedence as or higher precedence than operator.
b. Add operator to Stack.
[End of If]
6. If a right parenthesis is encountered ,then:
a. Repeatedly pop from Stack and add to P each operator (on the top of Stack) until a left
parenthesis is encountered.
b. Remove the left Parenthesis.
[End of If]
[End of step2 loop]
7. Exit
Let’s take an example to understand the algorithm
Infix Expression: A+ (B*C-(D/E^F)*G)*H, where ^ is an exponential operator.

Resultant Postfix Expression: ABC*DEF^/G*-H*+

Converting an Infix (operator is in between two operands) expression to Postfix (operator is after the operands)
can be done with or without using stack. Consider the following Infix expression:
A+B*C.
Find out the operators and their precedence. Here, * has more precedence. So solve (B*C) at the beginning. So,
the resultant postfix can be BC*.Now the expression can be written as A+BC*. For operator +, we have two
operands A & BC*. So, after conversion, the expression looks like ABC*+.

Here, although it is a small expression, we need to scan it again and again till we get the correct output. This is
time consuming.
On the other hand, if we would have used a stack, the expression needs to be scanned only once, so that it can be
brought to the required form.

APPLICATION OF STACKS: Recursion


Recursion is the process of repeating in a self-similar fashion. Objects that contain self-similar smaller
"copies" of themselves are recursive. In simple words, here a function or method has the ability of
calling itself to solve the problem.
A process in which a function calls itself could happen directly as well as indirectly. If a function calls
itself, it’s known as direct recursion. If the function f1 calls another function f2 and f2 calls f1 then it
is indirect recursion. Some of the problems that can be solved using recursion include DFS of Graph,
Different Types of Tree Traversals etc.,
When a function is called within the same function, it is known as recursion. The function which calls
the same function is known as recursive function.
Recursion can be explained still better as follows: Suppose P is a procedure containing either a Call
statement to itself or a Call statement to a second procedure that may eventually result in a Call
statement back to the original procedure P. Then P is called as a recursive procedure. To make the
program not to run indefinitely, a recursive procedure must have the following two properties:

1. There must be certain criteria called base criteria, for which the procedure does not call itself.
2. Each time, the procedure does call itself (directly or indirectly), it must be closer to the base
criteria.
A recursive procedure with these two properties is said to be well-defined.
Similarly, a function is said to be recursively defined if the function definition refers to itself. Again, in
order for the definition not to be circular, it must have the following two properties:
1. There must be certain arguments called base values for which the function does not refer to itself.
2. Each time the function does refer to itself, the argument of the function must be closer to a base
value.
A recursive function with these two properties is also said to be well defined.
Many programming languages implement recursion by means of stacks. Generally, whenever a
function (caller) calls another function (callee) or itself as callee, the caller function transfers
execution control to the callee. This transfer process may also involve some data to be passed from
the caller to the callee. This implies, the caller function has to suspend its execution temporarily and
resume later when the execution control returns from the callee function. Here, the caller function
needs to start exactly from the point of execution where it puts itself on hold. It also needs the exact
same data values it was working on. For this purpose, an activation record (or stack frame) is created
for the caller function.
Factorial Function
The product of positive integers from 1 to n, inclusive is called “n factorial” and is usually denoted by n!
n! = 1 * 2 * 3 * 4 * …. (n-2)* (n-1) * n
It’s convenient to define 0! = 1, so that the function is defined for all non-negative integers. So, we
have:
0! = 1 1!=1 2!=1*2=2 3!=1*2*3=6 4!=1*2*3*4=24 5!=1*2*3*4*5=120 and so on.
This is true for every positive integers n. i.e. n!=n*(n-1)!
Accordingly, the factorial function may also be defined as follows:
Definition: a) if n=0 then n! = 1
b) if n>0, then n! = n*(n-1)!
Observe that this definition of n! is recursive. Since it refers to itself when it uses (n-1)!. However, in
(a) the value of n! is explicitly given when n=0. (thus 0 is the base value) and in (b) the value of n! for
arbitrary n is defined in terms of smaller value of n which is closer to the base value 0. Accordingly, the
definition is not circular or in other words, the procedure is well defined.
Suppose P is a recursive procedure. During the running of an algorithm or a program which contains P,
we can associate level number with each given execution of procedure. The original execution of
procedure P is assigned to level 1 and each time procedure P is executed because of a recursive call,
its level is 1 more than the level of the execution that has made the recursive call. In the example
given above, Step 1 belongs to level 1. Hence, step 2 belongs to level 2, Step 3 to level 3, Step 4 to
level 4 and Step 5 to level 5. On the other hand, Step 6 belongs to level 4, since it is the result of a
return from level 5. In other words, step 6 and step 4 belongs to the same level of execution. Similarly
Step 7 belongs to level 3 and step 8 to level 2 and Step 9 to the original level 1. The depth of recursion
of a recursive procedure P with a given set of arguments refers to the maximum level number of P
during its execution.
The usual recursive code for calculating the factorial of a given number using recursion is as follows:
int fact(int n)
{
if(n==0)
return 1;

else
return n*fact(n-1);
}
Implementation of recursive procedures by stacks:
We know that a subprogram can contain both parameters and local variables. The parameters are the
variables which receive values from objects in the calling program, called arguments, and which
transmit the values back to the calling program. Besides, the parameters and local variables, the
subprogram must also keep track of the return address in the calling program. This return address is
essential, since control must be transferred back to its proper place in the calling program. At the time
that the subprogram is finished executing and control is transferred back to the calling program, the
values of the local variables and the return address are no longer needed.
Suppose the subprogram is a recursive program, then each level of execution of the subprogram may
contain different values for the parameters and local variables and for the return address.
Furthermore, if the recursive program does call itself, then these current values must be saved, since
they will be used again when the program is reactivated.
Suppose a programmer is using a high-level language which admits recursion such as C, then the
computer handles the book keeping that keeps track of all the values of the parameters, local
variables and return addresses. On the other hand, if a programmer is using a high level language
which does not admit recursion such as FORTRAN, then the programmer must set up the necessary
book keeping by translating the recursive procedure into a non-recursive one.
Stack implementation of factorial using recursion can be shown diagrammatically as follows:
fact(0)
fact(1)
fact(2)
fact(3)
fact(4)
We are pushing each and every recursive calls into the stack as and when it gets activated. The
function is said to be active till it is there in the stack. For example, in the given diagram, fact (4) will
be active till fact (3) is not solved. (i.e. fact (4) cannot be calculated till fact(3) is calculated). Similarly,
fact (3) is active till fact (2) is not solved and so on it continues till the top of the stack.
Once fact (0) is solved, its result is popped out and gets stored in a variable which in turn will be used
for calculating fact (1). Like this the output of the element popped will be used as an input for
performing the task by the next active element in the stack. Finally, fact (4) will be calculated and its
result will be popped and shown on the screen. i.e. the process comes to an end when no more
elements are left in the stack to pop.
Fibonacci Sequences
The Fibonacci sequence (usually denoted by F0, F1, F2,….) is as follows:
0 1 1 2 3 5 8 13 21 34 55 .. ..

That F0 = 0 and F1 = 1 and each succeeding term is the sum of the two preceding terms. A formal
definition of this function is:
Definition: (a) if n = 0 or n = 1 then Fn = n
(b) if n>1, then Fn = Fn-2 + Fn-1
This is an example of a recursive definition, since the definition refers to itself when it uses F n-2 and Fn-1.
Here in (a) the base values are 0 and 1 and in (b) the values of Fn is defined in terms of smaller values
of n which are closer to the base values. Accordingly, this function is well defined.
A procedure for finding the nth term Fn of the Fibonacci sequence is as follows:
FIBONACCI(FIB,N)
1. If N = 0 or N = 1, then set FIB = N and return
2. Else Call FIBONACCI(FIBA, N-2)
3. Call FIBONACCI(FIBB, N-1)
4. Set FIB = FIBA + FIBB
5. Return

Stack implementation of generating Fibonacci using recursion is shown here diagrammatically.

Assumption: We need to display the Fibonacci series up to the 4th term.

The stack representation of the above solution can be given as follows:


fib(0)
fib(1)
fib(2)
fib(3)
fib(4)

To solve fib(4), we need to solve fib(3) and fib(2). Similarly, to solve fib (3), we need to solve fib(2) &
fib(1) and so on. So fib(4) is active in the stack till fib(3) and fib(2) is solved. Similarly fib(3) is active till
fib(2) and fib(1) is solved and so on. Once fib(0) and fib(1) is solved, we can solve fib(2). Using fib(1)
and fib(2), fib(3) can be solved. It proceeds till the end in the same pattern.

This activation record keeps the information about local variables, formal parameters, return address
and all information passed to the caller function.

Tower of Hanoi:
Tower of Hanoi, is a mathematical puzzle which consists of three towers (pegs) and more than
one rings is as depicted −

These rings are of different sizes and stacked upon in an ascending order, i.e. the smaller one
sits over the larger one. There are other variations of the puzzle where the number of disks
increase, but the tower count remains the same.

Rules
The mission is to move all the disks to some another tower without violating the sequence of
arrangement. A few rules to be followed for Tower of Hanoi are −

 Only one disk can be moved among the towers at any given time.
 Only the "top" disk can be removed.
 No large disk can sit over a small disk.
Algorithm
To write an algorithm for Tower of Hanoi, first we need to learn how to solve this problem with
lesser amount of disks, say → 1 or 2. We mark three towers with
name, source, destination and aux (only to help moving the disks). If we have only one disk,
then it can easily be moved from source to destination peg.
If we have 2 disks −

 First, we move the smaller (top) disk to aux peg.


 Then, we move the larger (bottom) disk to destination peg.
 And finally, we move the smaller disk from aux to destination peg.

So now, we are in a position to design an algorithm for Tower of Hanoi with more than two disks.
We divide the stack of disks in two parts. The largest disk (nth disk) is in one part and all other (n-
1) disks are in the second part.
Our ultimate aim is to move disk n from source to destination and then put all other (n1) disks
onto it. We can imagine to apply the same in a recursive way for all given set of disks.
The steps to follow are −
Step 1 − Move n-1 disks from source to aux
Step 2 − Move nth disk from source to dest
Step 3 − Move n-1 disks from aux to dest
A recursive algorithm for Tower of Hanoi can be driven as follows –

START
Procedure Hanoi(disk, source, dest, aux)

IF disk == 1, THEN
move disk from source to dest
ELSE
Hanoi(disk - 1, source, aux, dest) // Step 1
move disk from source to dest // Step 2
Hanoi(disk - 1, aux, dest, source) // Step 3
END IF

END Procedure
STOP

Pictorial Representation of How Tower of Hanoi works

Let’s see the below example where we have three disks that have to move in the destination tower
which is the middle one with the help of the auxiliary tower.
Initial

1)Move disk 1 from tower source to tower destination

Source->Destination

2)Move disk 2 from tower source to tower auxiliary

source -> auxiliary

3) Move disk 1 from tower destination to tower auxiliary


destination ->auxiliary

4) Move disk 3 from tower source to tower destination

source -> destination

5)Move disk 1 from tower auxiliary to tower source

auxiliary -> source

6)Move disk 2 from tower auxiliary to tower destination


auxiliary -> destination

7) Move disk 1 from tower source to tower destination

source -> destination

You might also like