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

Unit 2 - Stack

Stack is a linear data structure that follows LIFO (Last In First Out) or FILO (First In Last Out) order. It has important applications in function calls, memory allocation, undo operations, and more. A stack can be implemented using either an array or linked list. Common stack operations include push to add an element and pop to remove the top element.

Uploaded by

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

Unit 2 - Stack

Stack is a linear data structure that follows LIFO (Last In First Out) or FILO (First In Last Out) order. It has important applications in function calls, memory allocation, undo operations, and more. A stack can be implemented using either an array or linked list. Common stack operations include push to add an element and pop to remove the top element.

Uploaded by

Amutha Rajini
Copyright
© © All Rights Reserved
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
You are on page 1/ 77

STACK IN DATA

STRUCTURES
STACK :DEFINITION
Stack is a linear data structure which follows a particular order in
which the operations are performed. The order may be LIFO(Last In
First Out) or FILO(First In Last Out).
WHY STACK?
Stack in PL:
• The return address (when the function is complete it returns back to
the function call)
• Arguments passed to the function
• Local variables of the function
Hardware Implementation of stack:
consists of reserved contiguous region of memory with a stack pointer into
that memory.
The stack has a fixed location in memory at which it begins.
The stack pointer is a hardware register that points to the current extents of
the
stack.
There are stacks that grow downwards and ones that grow upwards.
The stack can technically be located anywhere in memory, depending on the
system.
WHY STACK?
Usage in Microprocessors
The stack shares the available RAM memory with the heap and
the static memory area.
The C runtime library takes care of things that need to be defined
before the main() function can be executed.
One of those things is setting up the stack by loading the stack
pointer register with the start address of the stack.
The CPU has special instructions for pushing values onto the
stack and popping them back.
Implementation of stack
The stack implementation can be done using
(i)Array :Array is a static data structure so the collection of data must be
fixed in size.
The only problem with this implementation is array size must be
specified initially.
struct stack
{
int
stk[MAXSIZE];
int top;
};
• Implementation using Linked List :Linked List is a dynamic data
structure. So collection of data can be used which are variable in size
and structure. The program executes can grow and shrink to
accommodate the data being stored.

struct Node {
int data;
struct Node* link;
};
struct Node* top;
Drawback of Linked List implementation
• All the operations take constant time
• Calls to malloc and free are expensive especially in comparison to the
pointer manipulation routines.

Limitation of Array Implementation:


The stack cannot grow and shrink dynamically as per the requirement.
Operations on Stack
The basic operations on stack are
1.Push()
2.Pop()
Push() operation
• push() function is used to insert an element at the top of the stack.
• The element is added to the stack container and the size of the stack is
increased by 1.
• Before Performing Push() operation the stack condition to be checked for
overflow.
int isfull()
{
if(top ==MAXSIZE)
return 1;
else
return 0;
}
Step 1- START
Step 2- Store the element to push into array
Step 3- Check if top== (MAXSIZE-1) then stack is full else goto step 4
Step 4- Increment top as top = top+1
Step 5- Add element to the position stk[top]=num
Step 6- STOP
Pop() operation
 Deletion of an element from the top of the stack is called pop operation. The value of the
variable top will be decremented by 1 whenever an item is deleted from the stack.
 The top most element of the stack is stored in an another variable and then the top is
decremented by 1.
 The operation returns the deleted value that was stored in another variable as the result.
 Before performing pop() operation the stack condition must be checked for underflow.
int isempty()
{
if(top == -1)
return 1;
else
return
0;
}
Step 1 − Checks if the stack is empty.
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.
int pop()
{
if(!isempty())
{
int data = stack[top];
top = top - 1;
return data;
} else
return 0;
Applications of Stack

• Evaluating Arithmetic Expression


• Conversion of infix to Postfix expression
• Backtracking Procedure
• During Function call and return Procedure
Evaluating Arithmetic
• StackExpression
organized computers are better suited for post-fix notation than
the traditional infix notation. Thus the infix notation must be
converted to the post-fix notation.
• Expressions are usually represented in what is known as Infix
notation, in which each operator is written between two operands (i.e.,
A + B).
• we must distinguish between ( A + B )*C and A + ( B * C ) by
using either parentheses or some operator-precedence convention.
• Polish notation (prefix notation) –
It refers to the notation in which the operator is placed before its
two operands . Here no parentheses are required, i.e.,
+AB
• Reverse Polish notation(postfix notation) –
It refers to the analogous notation in which the operator is placed
after its two operands. Again, no parentheses is required in Reverse
Polish notation,
i.e., AB+
There are 3 levels of precedence for 5 binary
operators Highest: Exponentiation (^)
Next highest: Multiplication (*) and
division (/) Lowest: Addition (+) and
Subtraction (-)
For ex:
Infix notation: (A-B)*[C/(D+E)+F]
Post-fix notation: AB- CDE +/F
+*
• We first perform the arithmetic inside
the parentheses (A-B) and (D+E).
• The division of C/(D+E) must done
prior to the addition with F
•After that multiply the two terms inside the parentheses and bracket.
The procedure for getting the result is:
• Convert the expression in Reverse Polish notation( post-fix
notation).
• Push the operands into the stack in the order they are appear.
• When any operator encounter then pop two topmost operands for
Infix notation: (2+4) * (4+6)
Post-fix notation: 2 4 + 4 6 + *
Result: 60
Conversion of infix to postfix
• Infix expression: The expression of the form a op b. When an operator
is in-between every pair of operands.
• Postfix expression: The expression of the form a b op. When an
operator is followed for every pair of operands.

• Why postfix representation of the expression?


The compiler scans the expression either from left to right or
from right to left.
• Consider the below expression: a op1 b op2 c op3 d
If op1 = +, op2 = *, op3 = +
The compiler first scans the expression to evaluate the expression b * c,
then again scan the expression to add a to it.
The result is then added to d after another scan.
The repeated scanning makes it very in-efficient.
• It is better to convert the expression to postfix(or prefix) form before
evaluation.
• The corresponding expression in postfix form is: abc*+d+.
Algorithm
• 1. Scan the infix expression from left to right.
• 2. If the scanned character is an operand, output it.
• 3. Else,
…..3.1 If the precedence of the scanned operator is greater than the precedence of the
operator in the stack(or the stack is empty or the stack contains a ‘(‘ ), push it.
…..3.2 Else, Pop all the operators from the stack which are greater than or equal
to in
precedence than that of the scanned operator. After doing that Push the scanned
operator to
the stack. (If you encounter parenthesis while popping then stop there and push the
scanned operator in the stack.)
• 4. If the scanned character is an ‘(‘, push it to the stack.
• 5. If the scanned character is an ‘)’, pop the stack and and output it until a ‘(‘ is
encountered, and discard both the parenthesis.
• 6. Repeat steps 2-6 until infix expression is scanned.
• 7. Print the output
• 8. Pop and output from the stack until it is not empty.
Linked List Implementation - Initial
Stack
Linked List Implementation-Push()
void pop()
{
struct Node* temp;
if (top == NULL) {
printf("\nStack
Underflow“);
exit(1);
}
else {
temp = top;
top = top->link;
temp->link = NULL;
free(temp);
}
}
The Stack ADT
Stack Model
A stack is a list with the restriction that inserts and deletes can be
performed in only one position, namely the end of the list called the top.

Operations on a stack
1. push, which is equivalent to an insert,
2. pop, which deletes the most recently inserted element.

Stacks are sometimes known as LIFO (last in, first out) lists.
Implementation of Stacks

1. Array Implementation of Stacks


2. Linked List Implementation of Stacks
Array Implementation of Stacks

1. Array size should be specified.


2. The top of stack, top, which is -1 for an empty stack.
3. To push some element x onto the stack, we increment top and
then set STACK[top] = x, where STACK is the array representing
the actual stack.
4. To pop, we set the return value to STACK[top] and then
decrement top.
https://yongdanielliang.github.io/animation/web/Stack.html
Empty stack declaration
struct stack_record
{
unsigned int size;
int top;
element_type *stack_array;
};
typedef struct stack_record *STACK;
STACK create_stack( unsigned int max_elements )
{
STACK S;
S = (STACK) malloc( sizeof( struct stack_record ) );
if( S == NULL )
fatal_error("Out of space!!!");
S->stack_array = (element_type *)
malloc( sizeof( element_type ) * max_elements );
if( S->stack_array == NULL )
fatal_error("Out of space!!!");
Stack_array[2]
S->top = -1;
Stack_array[1]
S->size = max_elements; Stack_array[0]
return( S ); } top=-1
Size =3
Stack_array[2]
Routine for empty stack: Stack_array[1]
int is_empty( STACK S ) Stack_array[0]

{ top=-1
So Empty
Size =3
return( S->top == -1);
}
Routine for full stack:
int is_full( STACK S ) top= 2 C Stack_array[2]
Size =3 B Stack_array[1]
{ A Stack_array[0]
return( S->top ==size-1);
} So Full
Push Operation
void push( element_type x, STACK S )
{
if( is_full( S ) )
error("Full stack"); top==size-1
2==2 Satisfied
else
S->stack_array[ ++S->top ] = x;
}
top++ =>top=2 A Stack_array[2]
top++ =>top=1 A Stack_array[1]
top++ =>top=0 A Stack_array[0]

top=-1
Size =3
Pop Operation

void pop( STACK S )


{ top==-1
2==-1 Not
-1==-1 satisfied
satisfied
if( is_empty( S ) )
error("Empty stack");
else
top=2 A Stack_array[2]
S->top--; top-- =>top=1 A Stack_array[1]
top-- =>top=0 A Stack_array[0]
}
top=-1
Size =3
Linked List Implementation of Stacks
• Implementation of a stack uses a singly linked list.
• We perform a push by inserting at the front of the list.
• We perform a pop by deleting the element at the front of the
list.
• A top operation merely examines the element at the front of
the list, returning its value.
https://www.cs.usfca.edu/~galles/visualization/StackLL.html
• Creating an empty stack is also simple. We merely create a header node;
• make_null sets the next pointer to NULL

/* Stack implementation will use a header. */

typedef struct node *node_ptr;


struct node
{
element_type element;
node_ptr next;
};
typedef node_ptr STACK;
Stack creation
STACK create_stack( void )
{ next
STACK S;
S = (STACK) malloc( sizeof( struct node ) ); STACK/Header/Top
if( S == NULL )
fatal_error("Out of space!!!");
return S;
}
Make stack header to null
void make_null( STACK S )
{
if( S != NULL ) next NULL
S->next = NULL;
else STACK/Header/Top
error("Must use create_stack first");
}
Routine to check for empty stack:
int is_empty( STACK S )
{
return( S->next == NULL );
}
Next NULL

S/Header/Top

So It is empty
void push( element_type x, STACK S)
{
node_ptr tmp_cell; next next
tmp_cell = (node_ptr) malloc( sizeof ( struct node ) tmp_cell tmp_cell
);
if( tmp_cell == NULL )
fatal_error("Out of space!!!");
else
B A
{
tmp_cell->element = x;
tmp_cell->next = S->next;
S->next = tmp_cell;
} Next NULL
}
S/Header/Top
void pop( STACK S )
{
node_ptr first_cell;
if( is_empty( S ) ) S->next==NULL
error("Empty stack"); Not satisfied
satisfied
else
{
first_cell = S->next;
S->next = S->next->next;
free( first_cell );
}
}
next B next A next NULL

S/Header/Top first_cell first_cell


Applications

1. Balancing Symbols
2. Infix to Postfix Conversion
3. Postfix expression evaluation
4. Function Calls
Postfix expression evaluation

Get a postfix expression then follow the steps to


evaluate it.
1. When a number ( an operand) is seen, it is pushed onto the
stack
2. when an operator is seen, the operator is applied to the two
numbers (operands) that are popped from the stack and the
result is pushed onto the stack
For instance, the postfix expression
6523+8*+3+*
is evaluated as follows: The first four symbols
are placed on the stack. The resulting stack is

tos -> 2
tos -> 5 5
tos -> 6 6 6
Next a '+' is read, so 3 and 2 are popped from
the stack and their sum, 5, is pushed.

6523+8*+3+*
Next 8 is pushed.
Now a '*' is seen, so 8 and 5 are popped as 8 * 5 = 40 is pushed.

6523+8*+3+*

Next a '+' is seen, so 40 and 5 are popped and 40


+ 5 = 45 is pushed.
Now, 3 is pushed.

6523+8*+3+*

Next '+' pops 3 and 45 and pushes 45 + 3 = 48.


Finally, a '*' is seen and 48 and 6 are popped, the result 6 * 48 =
288 is pushed.

6523+8*+3+*

The time to evaluate a postfix expression is O(n),


because processing each element in the input consists
of stack operations and thus takes constant time.
• A*(B+C )*D+E Direct Push:
1.Empty stack
2.Greater
precedence
+ )
(
Stack

A B C + * D * E +

Output
Infix to Postfix Conversion

Scan the given infix expression from left to right.


1. If the scanned character is an operand, then output it.
2. If the scanned character is an operator and if the precedence of the scanned
operator is greater than the precedence of the operator in the top of the
stack ,push it.
3. Else, Pop all operators from the stack whose precedence is greater than or equal
to that of the scanned operator. Then, push the scanned operator to the top of
the stack.
4. If the scanned character is ‘(‘, push it to the stack.
5. If the scanned character is ‘)’, pop the stack and and output characters until ‘(‘ is
encountered, and discard both the parenthesis.
6. Repeat steps 1-6 until infix expression is scanned completely.
7. Pop and output from the stack.
For example:
Infix expression: a + b * c + ( d * e + f ) * g
First, the symbol a is read, so it is passed through to the output. Then '+' is
read and pushed onto the stack. Next b is read and passed through to the
output.

Next a '*' is read. The top entry on the operator stack has lower precedence than '*', so nothing is
output and '*' is put on the stack. Next, c is read and output. Thus far, we have
The next symbol is a '+'. Checking the stack, we find that we will pop a '*' and
place it on the output, pop the other '+', which is not of lower but equal
priority, on the stack, and then push the '+'.

The next symbol read is an '(', which, being of highest precedence, is placed on the stack. Then d is
read and output.
We continue by reading a '*'. Since open parentheses do not get removed
except when a closed parenthesis is being processed, there is no output.
Next, e is read and output.

The next symbol read is a '+'. We pop and output '*' and then push '+'. Then we read and output f
Now we read a ')', so the stack is emptied back to the '('. We
output a '+'.

We read a '*' next; it is pushed onto the stack. Then g is read and output.
The input is now empty, so we pop and output symbols from the
stack until it is empty.

This conversion requires only O(n) time and works in one pass through the input.
The Queue ADT
Like stacks, queues are lists. With a queue,
however, insertion is done at one end, whereas
deletion is performed at the other end.
Queue Model
• The basic operations on a queue are enqueue, which inserts an
element at the end of the list (called the rear), and
• dequeue, which deletes (and returns) the element at the start
of the list (known as the front).
Implementations of a queue
1.Array implementation
https://www.cs.usfca.edu/~galles/visualization/QueueArray.html
2.Linked list implementation
https://www.cs.usfca.edu/~galles/visualization/QueueLL.html
Array Implementation

struct queue_record
{
unsigned int q_front;
unsigned int q_rear;
unsigned int q_size;
element_type *q_array;
};
typedef struct queue_record * QUEUE;
QUEUE create_queue ( unsigned int max_elements )
{
QUEUE q;
q = (QUEUE) malloc( sizeof( struct stack_record ) );
if( q == NULL )
fatal_error("Out of space!!!");
q->q_array = (element_type *)
malloc( sizeof( element_type ) * max_elements );
if( q>q_array == NULL )
fatal_error("Out of space!!!");
q->front = -1;
q- >rear=-1;
q->size = max_elements;
return( q ); }

q_array [0] q_array [1] q_array [2] q_array [3] q_array [4]

front=-1
rear=-1
Size=5
Routine for empty Queue:
int is_empty( QUEUE q )
{
return( q->front == -1) ||q->front>q->rear);
}

q_array [0] q_array [1] q_array [2] q_array [3] q_array [4]

front=-1 rear=4 front=5


rear=-1 So Queue is empty
Size=5
Routine for full stack:
int is_full( QUEUE q )
{
return( q->rear==q->size-1);
}

q_array [0] q_array [1] q_array [2] q_array [3] q_array [4]

A B C D E
front=0 rear=4
Size=5 q->rear==q->size-1

So it is full
void enqueue( element_type x, QUEUE q )
{
if( is_full( q ) ) q->rear==q->size-1

error("Full queue"); Not satisfied


else satisfied
{
If(q->front==-1) So it is full,we can not
q->front++ insert further.

q->rear ++;
q->q_array[ q->rear ] = x;
} q_array [0] q_array [1] q_array [2] q_array [3] q_array [4]
} A B C D E

front=-1 front=0 rear=1 rear=2 rear=3 rear=4


rear=-1 rear=0
Size=5
element type dequeue( QUEUE q )
{ ( q->front == -1) ||q->front>q->rear)

if( is_empty( q ) ) Not satisfied


error("Empty stack");
else
{
return(q->q_array[q->front++]); ( q->front == -1) ||q->front>q->rear)
} satisfied
}

q_array [0] q_array [1] q_array [2] q_array [3] q_array [4]

A B C D E
front=0 front=1 front=2 front=3 rear=4
Size=5 front=4 front=5
Linked List Implementation of Queue
• Implementation of a Queue uses a singly linked list.
• We perform an enqueue by inserting at the end of the list.
• We perform a dequeue by deleting the element at the front of
the list.
/* Queue implementation will use front and rear. */

typedef struct node *node_ptr;


struct node
{
element_type element;
node_ptr next;
};
typedef node_ptr front,rear,QUEUE;
Queue creation
void create_queue( void )
{
front=NULL;
rear=NULL; front
} NULL
rear
Routine to check for empty Queue:
int is_empty()
{
return( front == NULL );
}
front NULL

So It is empty
void enqueue( element_type x)
{
node_ptr tmp_cell;
tmp_cell = (node_ptr) malloc( sizeof ( struct node ) ); next
if( tmp_cell == NULL )
A NULL
fatal_error("Out of space!!!"); tmp_cell
else
{
tmp_cell->element = x;
tmp_cell->next=NULL:
if(front==NULL)
{
front=tmp_cell;
rear=tmp_cell;
}
else
{
rear->next=tmp_cell;
rear = tmp_cell;
} front NULL
}
} rear
void enqueue( element_type x)
{
node_ptr tmp_cell;
tmp_cell = (node_ptr) malloc( sizeof ( struct node ) );
next
if( tmp_cell == NULL )
fatal_error("Out of space!!!");
else tmp_cell
{
tmp_cell->element = x;
tmp_cell->next=NULL:
if(front==NULL)
{
front=tmp_cell;
rear=tmp_cell;
A next B
}
else front
{
rear->next = tmp_cell; rear
rear=tmp_cell;
}
} NULL
}
NULL
void dequeue( )
{
node_ptr first_cell;
if( is_empty() ) front==NULL
error("Empty queue"); Not satisfied
else
{
first_cell = front;
front= front->next;
free( first_cell );
}
}
C next B next A next NULL

front front rear


first_cell front
first_cell
Circular Queue

• Circular Queue is a linear data structure in


which the operations are performed based on FIFO (First In
First Out) principle and the last position is connected back to
the first position to make a circle.
• It is also called 'Ring Buffer'.
To perform the insertion of an element to the
queue, the position of the element is calculated
by the relation as
rear= (rear+1) % maxsize
Queue[rear]=value

To perform the deletion, the position of the front


pointer is calculated by the relation as
value=Queue[front]
front= (front+1) % maxsize
Empty Routine f = -1 r = -1
int empty()
4 0
{
return((f==-1)||(f>r)); 3
1
}

2
4 0

3
f 1

2 r
Full routine
int full()
{ 4
A
r
0 f
A
return((rear==size-1 &&
front==0) || (rear==front- 3 A
A 1

1)); A

} 2
Enqueue operation: f=-1 r=-1
void enqueue(int x)
{
r f
if(full()) 0
4
print “Full”; X
X
else
{
if(f==-1) 3 X X
1
{
f=0;
r=0; X
} 2
else
r=(r+1)%size;
a[r]=x;
}
}
r
Dequeue Operation f f=-1 r=-1
void dequeue()
{
((f==-1)||(f>r));
if(empty()) 4 0
print “Empty”; X
else
{
print a[f]; 3
1
if(f==r) X
{
f=-1; X
r=-1; 2 X
}
else X
f=(f+1)%size;
}
}
Priority queue

The priority queue is a data structure having a collection


of elements which are associated with ordering.

Types of priority queue


1.Ascending priority queue
It is a collection of elements in which the items can be inserted arbitrarily but only smallest element can be
removed.

2.Descending priority queue


It is a collection of elements in which the items can be inserted arbitrarily but only biggest element can be
removed.

In priority queue , the elements are arranged in any order and out of
which only the smallest or largest element is allowed for deletion
each time.
The implementation of priority queue can be done using arrays or linked list.
The data structure heap is used to implement the priority queue effectively.

You might also like