Вы находитесь на странице: 1из 8

CSE211 Lecture Notes

1
CSE 211: Data Structures

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.








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.

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
D
Top
C
B
D
Top
C
B
D
Top
C
B
A
Top
C
B
A
Top
Pop
C
B
A
Top
Pop
C
B
C
B
A
Push(D)

CSE211 Lecture Notes

2
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
postfx 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 aa alegal
[ ( ] ) r rr rillegal
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 fle.
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. 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.


CSE211 Lecture Notes

3
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.

Q. Why shall we use a stack?
A. The returns are made in the reverse order of calls.

Implementing Recursive Functions using Stacks
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.

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

r0
r1
r2
r3

CSE211 Lecture Notes

4
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

/* ADT Prototype Declarations */
STACK* createStack (void);
bool pushStack (STACK* stack, void* dataInPtr);
void* popStack (STACK* stack);
void* stackTop (STACK* stack);
bool emptyStack (STACK* stack);
bool fullStack (STACK* stack);
int stackCount (STACK* stack);
STACK* destroyStack (STACK* stack);

// Stack ADT Type Defintions
typedef struct node2
{
void* dataPtr;
struct node2* link;
} STACK_NODE;

typedef struct
{
int count;
STACK_NODE* top;
} STACK;
/* =============== createStack ==============
This algorithm creates an empty stack.
Pre Nothing
Post Returns pointer to a null stack
-or- NULL if overflow
*/
STACK* createStack (void)
{
// Local Definitions
STACK* stack;

// Statements
stack = (STACK*) malloc( sizeof (STACK));
if (stack)
{

CSE211 Lecture Notes

5
stack->count = 0;
stack->top = NULL;
} // if
return stack;
} // createStack
/* ================= pushStack ================
This function pushes an item onto the stack.
Pre stack is a pointer to the stack
dataPtr pointer to data to be inserted
Post Data inserted into stack
Return true if successful
false if underflow
*/
bool pushStack (STACK* stack, void* dataInPtr)
{
// Local Definitions
STACK_NODE* newPtr;

// Statements
newPtr = (STACK_NODE* ) malloc(sizeof( STACK_NODE));
if (!newPtr)
return false;

newPtr->dataPtr = dataInPtr;

newPtr->link = stack->top;
stack->top = newPtr;

(stack->count)++;
return true;
} // pushStack
/* =================== popStack ==================
This function pops item on the top of the stack.
Pre stack is pointer to a stack
Post Returns pointer to user data if successful
NULL if underflow
*/
void* popStack (STACK* stack)
{
// Local Definitions
void* dataOutPtr;
STACK_NODE* temp;

// Statements
if (stack->count == 0)
dataOutPtr = NULL;
else
{
temp = stack->top;
dataOutPtr = stack->top->dataPtr;
stack->top = stack->top->link;
free (temp);
(stack->count)--;
} // else
return dataOutPtr;
} // popStack


CSE211 Lecture Notes

6
Some of the functions are not shown here.

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 conventional 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 infx 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
- move the operators


Examples:
Infx RPN
A A
A-B AB-

CSE211 Lecture Notes

7
(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
than the top stack element, 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

CSE211 Lecture Notes

8
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

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.

Вам также может понравиться