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

3.1-All About Stacks

HOASHDOIASHD

Uploaded by

DRAGON
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
23 views

3.1-All About Stacks

HOASHDOIASHD

Uploaded by

DRAGON
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
You are on page 1/ 76

CS250: Data Structures and Algorithms

All about Stacks!


Fall 2024
BSDS-1A
Lecture Outline
• What is a Stack?
• Operations on a Stack
• Initialize a stack
• Push (insert) an element
• Pop (delete) an element
• Read the top element
• status: empty, full

• Array-based and linked-list based Implementation.

• Applications of Stack
• Reverse a sequence of number using stack
• Keep track of function calls in a program
• Testing the correctness of a mathematical expression
• Converting a mathematical expression from infix to postfix and prefix notations
• Solving a mathematical expression in postfix form

2
What is a Stack?
• Definition:
• Conceptually, a stack is a data structure that allows adding and
removing elements in a particular order

• More specifically, in an order such that items can be inserted or deleted to


the collection of already inserted items from one end only, called the top of
the stack

3
Stack
• Stack is a linear data structure in which input and output operations
are constrained at only at the top end.
• Insertion or removal of an item is allowed only at the top end.
• Only the element at the top end can be accessed

4
Stack
• Stack is said to have “First In, Last Out" (FILO) or “Last In, First Out”
(LIFO) behaviour meaning that the first item added to a stack will be
the last item removed from a stack

• Example:
• Which is the first coin to pick up from
• the stack of gold coins?

5
Basics of stack
• Size: The number of elements on the stack

• Top: Points to the top most element on the stack. This refers to NULL
if stack is empty or size = 0

Because we think of stacks in terms of the physical analogy,


we usually draw them vertically (so the top is really on top)

6
Stack Operations
• Primarily two operations performed on one end only

• Push: Add an item on the top of stack

• Pop: Remove an item at the top of stack

7
Stack Operations
• Push Concerns
• What if when the stack is full?
• If the push operation is called when a stack is already full, the condition is called “stack overflow”
• Pop Concerns
• What if the stack is empty
• If the pop operation is called when a stack is already empty, the condition is called “stack underflow”

Solution:
• Before any push, check if the stack is already filled or not
• If it is filled then generate error message e.g., Stack Overflow
• Before pop, check if stack is not already empty
• If it is empty then generate error message e.g., Stack Underflow
8
Stack Operations
push(item) // Push an item onto the stack

pop( ) // Pop the top item off the stack

isEmpty() // Return true if stack is empty

isFull() // Return true if stack is full

top( ) or peek() // Return value of top item without removing it


9
Applications
• Real life
• Stack of trays in cafeteria
• Piles of books in library

• Computer Science
• Compilers use stacks to evaluate expressions & syntax parsing
• Program execution stack
• Undo operations
• Expression conversion
• Infix to post-, pre-fix and vice versa
• Tower of Hanoi
• And many more …
10
Choice of implementation
• Array based: Stack Maximum stack size is known ahead of time

• Linked List based: Stack Maximum stack size unknown

11
Array based Stack

12
Array based Stack
• When using an array to implement a stack

• The array's first element should represent the bottom of the stack

• The last occupied location in the array represents the stack's top

• This avoids shifting of elements of the array when we push or remove


elements from stack

13
Array based stack
• Allocate an array of some size (pre-defined)
• Maximum N elements in stack

• Bottom stack element stored at element 0

• Last index in the array is the top

• Increment top when one element is pushed, similarly decrement after


each pop
14
Array based stack operations
int stack[size]; int Pop() {
int top = -1; int element = -1;
void Push(int element) { If (!isEmpty()) {
if (!isFull()) { element = stack[top];

stack[++top] = element; stack[top--] = -1;


}
}
return element;
}
}
bool isFull() {
bool isEmpty() {
return top==size-1;
return top<0;
}
}

15
Linked List based Stack

16
Linked List based implementation
• How can we implement Stack using linked lists?

• LIFO
• Push = Insert at head
• Pop = Remove at head
• IsEmpty = Empty List
• Peek()/Top() = reading element at the top without popping it out.
• Clear = Destroy List

17
Linked List based implementation
class node class stack
{ {
public: int size;
int value; node* top;
node* next; public:
}; //class node stack() {
size=0;
top=NULL;
} //default constructor
void push(int);
bool pop();
bool isEmpty();
};
18
Linked List based implementation
void stack::push(int el) int stack::pop()
{ { int d;
node *temp; if(isEmpty()) {
temp =new node; cout<<"\nStack is Empty\n";
if (top==NULL) { return -9999;
temp->next =NULL; }
} else {
else { Node *temp = top;
temp->next =top; top=top->next;
} d=temp->value;
temp->value=el; delete temp;
top=temp; size--;
size++; }
} return d;
} 19
Linked List based implementation
bool stack::isEmpty() void main ()
{ {
if(getStackSize()==0) ...
return true; stack();
stack.push(5);
...
return false; }
}

20
Applications of Stacks

21
Application: Recursive
Function Implementation

22
Function calls & recursive
implementation
• The state of each function, including main(), is characterized
• by the contents of all local variables,
• by the values of the function’s parameters, and
• by the return address indicating where to restart in the calling function

• The data area containing all this information is called an activation record or a stack
frame and is allocated on the run-time stack
• exists for as long as a function owning it is executing

• This record is a private pool of information for the function, a repository that stores all
information necessary for its proper execution and how to return to where it was called
from
23
Activation Record / Stack Frame
• If a function is called either by
main() or by another function,
then its stack frame is created on
the run-time stack

• The run-time stack always reflects


the current state of the function
• E.g., suppose that main() calls
function f1(), f1() calls f2(), and f2()
in turn calls f3(). If f3() is being
executed, then the state of the run-
time stack is as shown
24
Activation Record / Stack Frame
• An activation record usually contains the following information
• Values for all parameters to the function, location of the first cell if an array is passed or a
variable is passed by reference, and copies of all other data items;
• Local variables that can be stored elsewhere, in which case, the activation record contains
only their descriptors and pointers to the locations where they are stored;
• The return address to resume control by the caller, the address of the caller’s instruction
immediately following the call;
• A dynamic link, which is a pointer to the caller’s activation record;
• The returned value for a function not declared as void. Because the size of the activation
record may vary from one call to another, the returned value is placed right above the
activation record of the caller

It helps the program know where to return after the current function has finished
executing, and it connects different activation records on the call stack, helping 25
Evaluating Exponents Recursively
double power (double x, unsigned int n) /* 102 */
{
if (n == 0) /* 103 */
return 1.0; /* 104 */
else
return x * power(x,n-1); /* 105 */
}
int main()
{ ...
y = power(5.6,2); /* 136 */
...
} 26
Evaluating Exponents Recursively
double power (double x, unsigned int n) /* 102 */
{
if (n == 0) /* 103 */
return 1.0; /* 104 */
else Trace of the recursive
calls
return x * power(x,n-1); /* 105 */
}

int main()
{ ...
y = power(5.6,2); /* 136 */
...
}
27
First, the value of the second argument, i.e., 2, is checked,
and power() tries to return the value of 5.6 * power(5.6,1)

double power (double x, unsigned int n) { /* 102 */


if (n == 0) /* 103 */
return 1.0; /* 104 */
else
return x * power(x,n-1); } /* 105 */
28
Again first, the value of the second argument, i.e., 1, is
checked, and power() tries to return the value of 5.6 *
power(5.6,0)

double power (double x, unsigned int n) { /* 102 */


if (n == 0) /* 103 */
return 1.0; /* 104 */
else
return x * power(x,n-1); } /* 105 */
29
A stack pointer (SP) is a special type of pointer used in computer systems, specifically in the context of a stack. The stack is a data structure used to store temporary data such as function calls, return addresses, local
variables, and control information. The stack pointer points to the top of this stack, helping the system keep track of where new data should be pushed or where the latest data should be popped from

double power (double x, unsigned int n) { /* 102 */


if (n == 0) /* 103 */
return 1.0; /* 104 */
else
return x * power(x,n-1); } /* 105 */
30
Again, first, the value of the
second argument, i.e., 0, is
checked, and power()
returns the value 1

At this point, there are two


pending calls on the run-time
stack—the calls to power()—
that have to be completed

double power (double x, unsigned int n) { /* 102 */


if (n == 0) /* 103 */
return 1.0; /* 104 */
else
return x * power(x,n-1); } /* 105 */
31
5.6 * power(5.6,0)
= 5.6 * 1 = 5.6 is
computed

double power (double x, unsigned int n) { /* 102 */


if (n == 0) /* 103 */
return 1.0; /* 104 */
else
return x * power(x,n-1); } /* 105 */
32
5.6 * power(5.6,0)
= 5.6 * 1 = 5.6 is
computed

double power (double x, unsigned int n) { /* 102 */


if (n == 0) /* 103 */
return 1.0; /* 104 */
else
return x * power(x,n-1); } /* 105 */
33
5.6 * power(5.6,1)
= 5.6 * 5.6 = 31.36
is computed

double power (double x, unsigned int n) { /* 102 */


if (n == 0) /* 103 */
return 1.0; /* 104 */
else
return x * power(x,n-1); } /* 105 */
34
5.6 * power(5.6,1)
= 5.6 * 5.6 = 31.36
is computed

double power (double x, unsigned int n) { /* 102 */


if (n == 0) /* 103 */
return 1.0; /* 104 */
else
return x * power(x,n-1); } /* 105 */
35
Application: Validating
Expressions via Stack

36
Algebraic expression
• An algebraic expression is a legal combination of operands and the
operators
• Operand is the quantity (unit of data) on which a mathematical
operation is performed
• E.g., operand may be a variable like x, y, z or a constant like 5, 4,0,9,1 etc.
• Operator is a symbol which signifies a mathematical or logical
operation between the operands
• Example of familiar operators include +,-,*, /, ^
• Considering these definitions of operands and operators, an example
of expression may be x+y*z
37
Stacks in problem solving
• Consider a mathematical expression that includes several sets of
nested parenthesis, e.g.,
( x + (y – (a +b)) )
• We want to ensure that parenthesis are nested correctly and the
expression is valid
Validation
• There is an equal number of right and left parentheses
• Every right parenthesis is preceded by a matching left parenthesis
(A+B) * C Valid
A + B) Invalid
(A+B] Invalid 38
Rules
• Each left parentheses is the opening scope while each right
parenthesis is a closing scope

• Parentheses count = 0 at the end means that no scopes have been left
open and left and right parentheses exactly match

((A+B) (A*B)
122221 11110

Invalid expression Valid expression

39
Parsing parenthesis
• Let us change the problem slightly
• Three different kinds of scope delimiters exist
e.g., { x + (y – [a +b]) }

• The scope ender must be of same type as scope opener

• It is necessary to keep track of not only the count of scope but also the types

• A stack may be used to keep track of the types of scopes encountered

40
Stacks in validating expression
• Whenever a scope opener is encountered
• it is pushed into the stack
• Whenever a scope ender is encountered,
• The stack is examined
• If the stack is empty, the scope ender does not have a matching opener and
string is invalid
• If stack is not empty, we pop an item and check if it corresponds to scope
ender
• If match occurs we continue,
• otherwise the expression is invalid
• At the end of the string the stack must be empty
41
Valid = true
S = the empty stack // array of chars say char s[100]
While (we have not read the entire string) {
read the next symbol (symb) of the string;

Pseudo code
if (symb == ‘(‘ || symb == ‘[‘ || symb == ‘{‘)
push (s, symb);
if (symb == ‘)‘ || symb == ‘]‘ || symb == ‘}‘)
if (IsEmpty(s))
valid = false;
else {
I = pop (s);
if ( I is not the matching opener of symb)
valid = false;
}// end of else
} //end while
If (valid)
cout << “Valid String” << endl;
Else
cout << “not a valid string” 42
Stacks in validating expression
{ x + (y – [a +b]) }

( ( (

{ { { { {

43
1 2 3 4 5 6 7
Application: Infix to Postfix
conversion

44
Operator Priorities
• How do you figure out the operands of an operator?
• a+b*c
• a*b+c/d

• This is done by assigning operator priorities


• priority(*) = priority(/) > priority(+) = priority(-)

• When an operand lies between two operators, the operand is


associated with the operator that has the higher priority

45
Tie breaker
• When an operand lies between two operators that have the same
priority, the operand is associated with the operator on the left
• a+b-c
• a*b/c/d

46
Infix, Prefix & Postfix expressions
• INFIX: Expressions in which operands surround the operator,
• e.g., x+y, 6*3 … etc.

• PREFIX: Expressions in which operator comes before the operands,


e.g., +xy, *+xyz etc.
• Also known as Polish Notation (PN)

• POSTFIX: Expressions in which operator comes after the operands,


e.g., xy+, xyz+* etc.
• Also known as Reverse Polish Notation (RPN)
47
Prefix & Postfix notations, why to
use?
• Why to use these weird looking PREFIX and POSTFIX notations when
we have simple INFIX notation?

• INFIX notations are not as simple as they seem especially while evaluating
them
• To evaluate an infix expression we need to consider operators’ Priority and
Associative property
• E.g., expression 3+5*4 can either be evaluated as 32 i.e., (3+5)*4 or 23
i.e., 3+(5*4)
• To solve this problem Precedence or Priority of the operators were
defined to govern the evaluation order i.e., operator with higher
precedence is applied before an operator with lower precedence 48
Prefix & Postfix notations, why to
use?
• Compilers need to generate assembly code in which one operation is
executed at a time and the result is retained for other operations

• Therefore, all expressions have to be broken down unambiguously


into separate operations and put into their proper order

• That is where Polish notation is useful as it allows us to create an


expression tree, which imposes an order on the execution of
operations

49
Expression tree

Prefix
Infix
Postfix

50
Order of Precedence
• Following is the order of precedence for arithmetic operators ( From
highest to lowest )

51
Examples of Infix to Post- & Prefix

Example Infix PostFix Prefix

Example 1 A+B AB+ +AB

Example 2 (A+B) * (C + D) AB+CD+* *+AB+CD

Example 3 A-B/(C*D^E) ABCDE^*/- -A/B*C^DE

52
Infix to Postfix
Example 4:

A+B*C
A+(B*C) Parentheses for emphasis
A+(BC*) Convert the multiplication, Let D=BC*
A+D Convert the addition
AD+
ABC*+ Postfix Form

53
More examples

54
Infix to postfix conversion
Example 5
A+B-C =
[AB+]-C
AB+C-

Example 6
(A+B)*(C-D)
= [AB+]*(C-D)
= [AB+]*[CD-]
= AB+CD-*
55
Infix to postfix conversion
Example 7
A^B*C-D+E/F/(G+H)
= A^B*C-D+E/F/[GH+]
= [AB^]*C-D+E/F/[GH+]
= [AB^C*]-D+E/F/[GH+]
= [AB^C*]-D+[EF/]/[GH+]
= [AB^C*]-D+[EF/GH+/]
= [AB^C*D-]+[EF/GH+/]
= AB^C*D-EF/GH+/+

56
Infix to postfix conversion
Example 8
((A+B)*C-(D-E))^(F+G)
=([AB+]*C-[DE-])^[FG+]
=([AB+C*]-[DE-])^[FG+]
=[AB+C*DE--]^[FG+]
=AB+C*DE—FG+^

57
Infix to postfix conversion
Example 9
A-B/(C*D^E)
=A-B/(C*[DE^])
=A-B/[CDE^*]
=A-[BCDE^*/]
=ABCDE^*/-

58
Applications: Infix to
Postfix Conversion using
Stack

59
Infix to Postfix conversion algorithm
• Create an empty stack for keeping operators
• Create an empty list for output
• Scan each element of input expression from left to right
• If the element is an operand, append it to the end of the postfix string
• If the element is a left parenthesis, push it on the stack
• If the element is a right parenthesis, apply pop operation to the stack until the corresponding left
parenthesis is removed & append each operator to the end of the postfix string; when the left
parenthesis pops off the stack, discard it and the right parenthesis.
• If the current element is an operator, *, /, +, or -, push it on the stack if its priority is higher than the
symbol at the top of the stack. However, first remove any operators already on the stack that have
higher or equal precedence and append them to the postfix string.
• Note: When, left parenthesis appears on the top of the stack, it has the lowest precedence in
comparison to any other operator that is the current element.
• When the input expression has been completely processed, check the stack. Any operators still on the stack
60
can be removed and appended to the end of the postfix string.
Source: https://prepinsta.com/data-structures/infix-to-postfix-conversion/

61
Source: https://prepinsta.com/wp-content/uploads/2020/07/Infix-to-postfix-conversion.webp
62
Precendence Function
• Precedence Function : prcd(op1,op2)
• Op1 & Op2 represent operators +,-,*,/
• Assume we pass current operator as op1, and the operator at stack top as
op2.

• If op1 has higher precedence than op2 , the prcd(op1,op2) returns true. For
example prcd(‘*’,’+’) and prcd(‘/’,’+’) is true.

• Otherwise, prcd(op1,op2) returns FALSE. For example prcd(‘+’,’*’) is FALSE,


and prcd(‘+’, ‘+’) is FALSE

63
Algorithm to Convert an Expression
from Infix to Postfix Form
Opstk=the empty stack;
while (not end of input){
symb= next input character ; push(opstk , symb );
if (symb is an operand) } //end of else
add symb to the postfix string ;
else { } // end of while
while (!empty(opstk)&& prcd(symb,
stacktop(opstk))) { // output remaining operators
topsymb = pop (opstk) ; while (!empty(opstk)) {
add topsymb to the postfix string ; topsymb = pop(opstk) ;
} //end of while add topsymb to the postfix string ;
} //end of while 64
Suppose we want to convert 2*3/(2-1)+5*3 into Postfix form,
Expression Stack Output
2 Empty 2
* * 2
3 * 23
/ / 23*
( /( 23*
2 /( 23*2
- /(- 23*2
1 /(- 23*21
) / 23*21-
+ + 23*21-/
5 + 23*21-/5
* +* 23*21-/5
3 +* 23*21-/53
Empty 23*21-/53*+
65
Applications: Evaluating a
Postfix expression using
Stack

66
Evaluating Postfix expression
• Each operator in a postfix string refers to the previous two operands
in the string

• Suppose that each time we read an operand we push it into a stack.


When we reach an operator, its operands will then be the top two
elements on the stack

• We can then pop these two elements, perform the indicated


operation on them, and push the result back onto the stack so that it
will be available for use as an operand of the next operator
67
Algorithm to evaluate Postfix
expression
1) Create an empty stack
2) Scan the elements of Postfix expression from left to right
3) If the element is an operand, push the value onto the stack
4) If the element is an operator, *, /, +, or -
 Pop the stack twice. The first pop is the second operand and the second pop
is the first operand
 Perform the arithmetic operation & push the result back on the stack
5) When the input expression has been completely processed, apply
pop operation onto the stack to return the computed value

68
Algorithm to evaluate Postfix
expression
WHILE more input items exist
{
If symb is an operand
then push (stk,symb) // stk is the stack used
else //symbol is an operator
{
Opnd1=pop(stk);
Opnd2=pop(stk);
Value = result of applying symb to opnd1 & opnd2
Push(stk,value);
} //End of else
} // end while
Result = pop (stk); 69
Algorithm to evaluate Postfix
expression

70
Question
• Evaluate following Postfix expression:

623+-382/+*2^3+

Final answer is
a) 49
b) 51
c) 52
d) 7
e) None of these
71
Applications: Adding very
large integers

72
Adding very large numbers
• Consider adding very large numbers
• Since the largest magnitude of integers is limited (32 bits), so we are not able to
perform following:
18,274,364,583,929,273,748,459,595,684,373
+
8,129,498,165,026,350,236
because integer variables cannot hold such large values
• The problem can be solved if we
• treat these numbers as strings of numerals,
• store the numbers corresponding to these numerals on two stacks, and
• then perform addition by popping numbers from the stacks
73
74
Conclusion
• Stack is a LIFO or FILO data structure due to input and output operations constrained
only at the top end.

• Practical applications
• Stacks in validating expressions
• Infix to Postfix conversion & evaluation
• Postfix/Prefix forms do not rely on operator priorities, a tie breaker, or delimiters
• Easier evaluation of expressions
• Unambiguous expression tree
• Algorithms implementing stack
• To convert any Infix expression into its corresponding postfix or prefix expression
• To evaluate any postfix expression
• Adding very large integer numbers
75
CS250: Data Structures and Algorithms

Thank you

You might also like