Unit 2 - Stack
Unit 2 - Stack
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.
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
{ 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
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
1. Balancing Symbols
2. Infix to Postfix Conversion
3. Postfix expression evaluation
4. Function Calls
Postfix expression evaluation
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+*
6523+8*+3+*
6523+8*+3+*
A B C + * D * E +
Output
Infix to Postfix Conversion
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]
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
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
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. */
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
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
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.