You are on page 1of 9

CSE211 Lecture Notes – 2004/2005-II

CSE 211: Data Structures


Lecture Notes V
by
Ender Ozcan, Sebnem Baydere

STACKS

In this lecture we will discuss one of the most common data structures found in the computer
algorithms; stacks

Stacks are special cases of the more general data structures than an ordered list.

A stack is an ordered list in which all insertions and deletions are made at one end, called the top. In
other words it is a data structure in which all access is restricted to the most recently inserted elements.
Stack is dynamically changing data type. How does it change? As we know that the ADTs are defined
together with the operations defined on them. The last item added to the stack is placed on the top and
is easily accessible. The items that have been in a stack for a while are more difficult to access. The
stack structure is more appropriate if we expect to access only to the item at the top; all other items are
inaccessible.

It is analogues to the common stack of bills, stack of plates, stack of newspapers.

1. Stack Operations
In a stack the three natural operations are Insert, Delete and Find. These are renamed as Push
(insertAtFront), Pop (removeFromFront) and Top (peek). We can combine Pop and Top operations
and return the popped item in Top. We also need an operation to create the stack.

Pop A Push(D) D

A Top D Top
Top
B B B
C C C
We expect that each stack operation take constant amount of time, independent of the number of the
items in the stack. Analogy, in stack of newspapers finding today's paper takes fixed amount of time
no matter how deep the stack is.

1
CSE211 Lecture Notes – 2004/2005-II

What makes the stack useful is that there are many applications for which we need to access the most
recently inserted item to the list.

2. Example Uses of Stacks


• Compiler Design, matching symbols
• The stack is also used in the implementation of the function calls. When a call is made to a
function all the variables local to a function and the return address have to be saved by the
system. The problem is similar to the balanced symbols.
• Stacks are used in converting parenthesized and unparenthesized expressions from infix to
postfix notation based on a set of precedence rules.
• Another important application of stacks is the evaluation of expressions in computer languages.

2.1. Matching Symbols


Compilers check the errors in your programs, frequently a lack of one symbol (missing * or / from a
comment or a missing ( ) will cause the compiler to return unsuccessfully. A useful tool in this
situation is a program that checks if everything is balanced. i.e., every ( has a corresponding ) or
something else and the order is legal.
Solution: Count the number of opening symbols, and corresponding closing symbols. For each symbol
the quantities must be the same. Following structures will pass the legality check using this approach:
[ ( ) ]a legal
[ ( ] ) r illegal
Simply counting the symbols is not sufficient, because [ ( ) ] legal but [ ( ] ) is illegal. Note that we are
talking about processing a sequence of tokens and not worry about character constant '{' which does
not need a matching. A stack is useful here because we know that each closing symbol matches the
most recent opening symbol. If we place the opening symbols in a stack we can easily check that
closing symbols are balanced or not.

Algorithm:
1. Make an empty stack.
2. Read symbols until the end of the file.
3. If the token is an opening symbol, push it onto the stack.
4. If the token is a closing symbol and the stack is empty report error.
Otherwise, pop the stack.
5. If the symbol popped is not the corresponding opening symbol report an error
6. At the end of the file, if the stack is not empty report an error
Otherwise, the symbols are balanced.

2.2. Use of Stacks in Function calls

What happens when a function is called? Three actions are taken in steps:
1. A copy of the arguments is made locally within the function. Any changes to them are made on the
local copy.
2. Storage is allocated for the local variables.
3. Control is transferred to the function. Before passing the control to the fuction return address in the
calling program must also be saved. This is an implicit argument passed to the function. When the
function is finished it retrieves the return address in its own data space and passes the control to it.
When a function returns three actions are performed.
1. Return address is stored in a safe location.

2
CSE211 Lecture Notes – 2004/2005-II

2. Function`s data area is freed.


3. Finally, control is returned to the calling function and any returned value is also stored in known
registers.
Suppose that we have three functions and a main function.

Main Function A1 Function A2 Function A3


----- ------- ------ ------
----- ------ call A3 -------
call A1 ----- r3:------- -------
r1: ----- call A2 ------- --------
---- r2:------ ------- end
end ------- end
end
Main calls A1, on completion its execution main will start at r1. This address is passed to A1. It saves
it in some location for later processing.
A1 invokes A2
A2 invokes A3
In each case calling function passes the return address to the called procedure.

If we examine the memory while A3 is computing there will be an implicit stack which contains (r0,
r1, r2, r3) , where r0 is the address in O/S where main returns control.
r3
Q. Why shall we use a stack?
A. The returns are made in the reverse order of calls. r2
r1
Implementing Recursive Functions using Stacks r0
The use of stacks in implementing function calls becomes clerarer in this special case. If we assume
that the return address for each function is kept in hardware we have a problem implementing reentrant
code (recursive). Therefore, a separate stack of return addresses must be used and each time a
recursive function calls itself, a new data area for that call must be allocated containing all the
parameters and local variables.

3. Basic Stack Operations


Two changes that can be made on a stack are given special names. When an item is added to a stack, it
is pushed onto the stack, and when an item is removed it is popped from the stack. One way of
implementing stacks is using arrays. Assuming that a stack is implemented using an array, we can
define the following operations and use them whenever needed in an algorithm.

Given a stack, item i, maximum size of the stack as n and top being the variable that holds the current
number of elements in the stack, performing PUSH(stack,i,n top) operation adds the item i to the top
of the stack . Similary, POP(stack,n,top) removes the top element and returns it in i. It is also possible
to return the top element as a function value. For example: pop operation can be impelmented in a
away that

i=pop(stack,n,top) /* returns the top item in variable i.

3
CSE211 Lecture Notes – 2004/2005-II

The following procedures give the algorithms for these operations (considering that the stack is
implemented with an array of size n).

Procedure PUSH(item, stack, n, top)


//Insert item into the stack of maximum size n; top is the number
of elements currently in stack//

if top>= n then report STACK_FULL


else top = top +1;
stack(top) = item
end PUSH

Procedure POP(item, stack, top)


//removes the top element of stack and stores it in item unless stack is
empty //

if top<0 then report STACK_EMPTY


else item = stack(top)
top = top -1
end POP

Additional to these methods implementing peek (returns the value at the top of the Stack without
removing it), getStackSize, isEmpty and isFull (if a static structure is used) methods are very useful.

3.1. Implementation of Stacks in C


Before defining the operations we need to create an empty stack. An array can be a home for the stack.
During the execution satck can grow or shrink within this array. One end of the array is the bottom and
the insertions and deletions are made from the other end. We also need another field that at each point
we need to keep track of the current position of the top of the stack.
Therefore, a stack can be declared as a structure type containing two items: an array to hold the
elements of the stack and an integer to indicate the position of the current stack.
#define STACKSIZE 100
struct stackcontent {
int top;
myType items[STACKSIZE];
};
typedef struct stackcontent Stack;
Stack s;

The above definition tells us that the maximum size of a Stack type is given by the STACKSIZE
which is 100 in the given example. The identifier top indicates the top most item in the stack. For
example: if the value of s.top is 4 this means that there are five elements in the stack. s.items[0],
s.items[1], ...s.items[4]. When the stack is popped the value of the top is reduced to 3 which means 4
items remain in the stack.

The empty stack contains no elements and can be indicated by s.top= -1 .To initialize the stack we can
set the value of the top to -1.

We may write a function which returns if the given stack is empty or not.

4
CSE211 Lecture Notes – 2004/2005-II

#define true 1
#define false 0
int isEmpty(Stack *ps)
{
if (ps->top == -1) return(true);
else return(false);
};

Implementing the pop operation:


Poped value can be stored into a variable passed to pop operation as an argument and pop operation
can return success or failure of the operation.
#define SUCCESS 1
#define STACK_EMPTY -1
#define STACK_FULL -2
int pop(Stack *ps, myType &popedVal) {
if (isEmpty(ps) ) {
printf("%s", "stack EMPTY");
return STACK_EMPTY;
}
popedVal = ps->items[ps->top--]);
return SUCCESS;
}
Write the other stack operations in C as an exercise.

3.2. Implementation of Stacks in C++


Following interface can be used implementing a stack:
template< class myType >
class Stack {
private:
int size;
int top;
myType *stackArr;
public:
Stack( int );
~Stack();
bool isEmpty();
bool isFull();
int pop( myType & );
int push( myType & );
int peek( myType & );
int stackSize();
}

template< class myType >


bool
Stack <myType> :: isEmpty()
{
return (top == -1);
};

#define SUCCESS 1
#define STACK_EMPTY -1
#define STACK_FULL -2

5
CSE211 Lecture Notes – 2004/2005-II

template< class myType >


int Stack <myType> :: pop( myType &popVal ) {
if ( isEmpty() ) {
printf("%s", "stack EMPTY");
return STACK_EMPTY;
}
popVal = ps->items[ps->top--]);
return SUCCESS;
}

4. Evaluation of Expressions and Polish Notation

( ( (A/B)**C)+(D*E)-(A*C) )

How compiler executes this lengthy expression?

Every language uniquely define the order of operators. (Precedence rule)

An expression consists of operands operators and delimiters.

Parenthesized co nventional form is called infix form. (operand operator operand)


They are converted to another form called postfix form (Reverse Polish Notation -each operator
appears after the operands).

We will see the relationship between an infix and postfix notation, we will also see how stacks can be
used to evaluate an RPN expression. Also to convert an infix to postfix by hand.

RPN is used in interpreters and compilers as an intermediate representational form for statements.

There is also prefix form (Forward Polish Notation) in which the operator precedes its operands.

Ex A*B/C infix
AB*C/ postfix

Converting from infix to postfix notation:

Translation by hand:

1. Parenthesize the expression


2. Move all operators so that they replace their corresponding right paranthesis, starting from the
inner most parenthesis
3. Delete all parenthesis

( ( (A / (B*C) ) + (D*E) ) - (A*C ) )

ABC*/DE*+AC*-

Its implementation requires two passes - read and paranthesize

6
CSE211 Lecture Notes – 2004/2005-II

- move the operators

Examples:
Infix RPN
A A
A-B AB-
(A-B)+C AB-C+
A-(B+C) ABC+-
(A+B)*(C-D) AB+CD-*

If we relax the condition that expression be fully parenthesized. We need to use operator precedence
from left to right. (associative rule)

Associative Rule

1) + and - associate left to right


2) * and / associate left to right
3) If there is a built in exponentiation operator (**) associate from right to left (we will ignore this )

Priorities and Precedence Rule


1) + and - have the same priority
2) * and / have the same priority
3) (* and /) precede (+ and -)

Unparenthesized Assumed Parenthesized Form RPN


A+B+C (A+B)+C AB+C+
A-B+C (A-B)+C AB-C+
A+B-C+D ((A+B)-C)+D AB+C-D+
A*B*C (A*B)*C AB*C*
E/F*G (E/F)*G EF/G*
E*F/G*H ((E*F)/G)*H EF*G/H*
A+B*C+D A+(B*C)+D ABC*+D+
A-(B+C)*D A-((B+C)*D) ABC+D*-

4.1. Infix -to RPN Translation using STACKS

1) Initialize an empty stack of operators


2) While no error, until the end of infix expression do the following:
a) Get the next token (const., Variable, operator, “(“, “)”) from the infix expression
b) If token is
i) A left paranthesis Push it onto stack
ii) A right paranthesis Pop and display (add it to the rpn string) elements
until a left paran. is encountered. (if no left
paran., error!)
iii) An operator If the stack is empty or token has higher priority

7
CSE211 Lecture Notes – 2004/2005-II

than the top stack ele ment, push token onto


the stack.

Otherwise, opo and display (add it to the rpn string)


the top stack element, then repeat the
comparison of token with new top stack element.
Note: Assume “(“ in the stack
has lower priority than others
iv) An operand display it (add it to the rpn string)
3) When the end of infix expression is reached, pop and display (add it to the rpn string) stack items
until the stack is empty.

Note that do not display or add “(“ or “)” to the rpn string.

With operator priorities only

Example : Workout A+B*C/D-E


See COURSE NOTES..

Involving parenthesis in the expressions

Push left parenthesis onto the operator stack (create a false bottom). Apply the algorithm. When a right
parenthesis is reached the stack is emptied until the false bottom)

Example : Workout A+(B*(C-D)/E)


See COURSE NOTES

4.2. Evaluation of RPN

Assuming only binary operators, the algorithm for evaluating RPN expressions is:
Get a token until EOF
If token is a number (operand), push it into a stack
Else // it is an operator
Pop two numbers from the stack, otherwise exit with ERROR
Apply the operator
Push the result back onto the stack
Pop the result
If the stack is empty return the result, otherwise exit with ERROR

35+ 24 - * 6 *

1- empty 2- 3 3- 5 4- 8 5- 2 6-4 7- -2 8- -16


3 8 2 8
8

9- 6 10- -96
-16

8
CSE211 Lecture Notes – 2004/2005-II

Each snapshot is taken just before the leftmost input character is examined.
In general,
1. If argument is an operand, stack it.
2. If argument is an n-ary operator, then the n arguments are already on the stack. Pop the n arguments
from the stack and replace by the value of the operator applied to the arguments.

You might also like