C++ - Unit IV

Download as pptx, pdf, or txt
Download as pptx, pdf, or txt
You are on page 1of 50

Programming in C++ using Data Structures

Unit - IV
Stacks
Stack is a linear data structure which follows a particular order in which the operations are
performed. stack allows operations at one end only. For example, we can add or remove an element
from the top of the stack only. This feature makes it LIFO data structure.
LIFO stands for Last-in-first-out. Here, the element which is placed (inserted or added) last, is
accessed first. In stack terminology, insertion operation is called PUSH operation and removal operation
is called POP operation.
Stack Representation
The following diagram depicts a stack and its operations 

A stack can be implemented by means of


Array, Structure, Pointer, and Linked List.
Stack can either be a fixed size one or it may
have a sense of dynamic resizing. Here, we
are going to implement stack using arrays,
which makes it a fixed size stack
implementation.
Stacks … Cont’d
Basic Operations:
Stack operations may involve initializing the stack, using it and then de-initializing it. Apart from
these basic stuffs, a stack is used for the following two primary operations −
• push() − Pushing (storing) an element on the stack.
• pop() − Removing (accessing) an element from the stack.
When data is PUSHed onto stack.

To use a stack efficiently, we need to check the status of stack as well. For the same purpose, the
following functionality is added to stacks −

• peek() − get the top data element of the stack, without removing it.
• isFull() − check if stack is full.
• isEmpty() − check if stack is empty.

At all times, we maintain a pointer to the last PUSHed data on the stack. As this pointer always
represents the top of the stack, hence named top. The top pointer provides top value of the stack
without actually removing it.
Stacks … Cont’d
peek()
Algorithm of peek() function Implementation
begin procedure peek int peek()
return stack[top] {
end procedure return stack[top];
}
isfull()
Algorithm of isfull() function Implementation
begin procedure isfull bool isfull()
if top equals to MAXSIZE {
return true if(top == MAXSIZE)
else return true;
return false else
endif return false;
end procedure }
Stacks … Cont’d
isempty()
Algorithm of isempty() function Implementation
begin procedure isempty bool isempty()
if top less than 1 {
return true if(top == -1)
else return true;
return false else
endif return false;
end procedure }

Note : Implementation of isempty() function is slightly different. We initialize top at -1, as the index in array
starts from 0. So we check if the top is below zero or -1 to determine if the stack is empty.
Stacks … Cont’d
Push Operation
The process of putting a new data element onto stack is known as a Push Operation. Push operation
involves a series of steps −
Step 1 − Checks if the stack is full.
Step 2 − If the stack is full, produces an error and exit.
Step 3 − If the stack is not full, increments top to point next empty space.
Step 4 − Adds data element to the stack location, where top is pointing.
Step 5 − Returns success

If the linked list is used to implement the stack,


then in step 3, we need to allocate space
dynamically.
Stacks … Cont’d
Algorithm for PUSH Operation
begin procedure push: stack, data
if stack is full
return null
endif
top ← top + 1
stack[top] ← data
end procedure
Implementation
void push(int data) {
if(!isFull()) {
top = top + 1;
stack[top] = data;
} else {
cout << "Could not insert data, Stack is full” << “\n”;
}
}
Stacks … Cont’d
Pop Operation
Accessing the content while removing it from the stack, is known as a Pop Operation. In an array
implementation of pop() operation, the data element is not actually removed, instead top is
decremented to a lower position in the stack to point to the next value. But in linked-list implementation,
pop() actually removes data element and deallocates memory space.
A Pop operation may involve the following steps −
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.
Stacks … Cont’d
Algorithm for POP Operation
begin procedure pop: stack
if stack is empty
return null
endif
data ← stack[top]
top ← top - 1
return data
end procedure
Implementation
int pop(int data) {
if(!isempty()) {
data = stack[top];
top = top - 1;
return data;
} else {
printf("Could not retrieve data, Stack is empty.\n");
}
}
Stacks … Cont’d
Applications of stack:
The Stack has some important applications in different aspect. Few of them are
• Balancing of symbols
• Infix to Postfix /Prefix conversion
• Redo-undo features at many places like editors, photoshop.
• Forward and backward feature in web browsers
• Used in many algorithms like Tower of Hanoi, tree traversals, stock span problem, histogram problem.
• Other applications can be Backtracking, Knight tour problem, rat in a maze, N queen problem and sudoku
solver
• In Graph Algorithms like Topological Sorting and Strongly Connected Components
Different Types of Notations to represent Arithmetic expression:
The way to write arithmetic expression is known as a notation. An arithmetic expression can be written in
three different but equivalent notations, i.e., without changing the essence or output of an expression. These
notations are −
• Infix Notation
• Prefix (Polish) Notation
• Postfix (Reverse-Polish) Notation
These notations are named as how they use operator in expression.
Applications of Stacks … Cont’d
Infix Notation
In Infix notation, operators are used in-between operands. It is easy for us humans to read, write,
and speak in infix notation but the same does not go well with computing devices. An algorithm to
process infix notation could be difficult and costly in terms of time and space consumption.
Example. a - b + c
Prefix Notation
In this notation, operator is prefixed to operands, i.e. operator is written ahead of operands. For
example, +ab. This is equivalent to its infix notation a + b. Prefix notation is also known as Polish
Notation.
Example. -+a b c
Postfix Notation
In this notation, the operator is postfixed to the operands i.e., the operator is written after the
operands. For example, ab+. This is equivalent to its infix notation a + b. Postfix notation is also known as
Reversed Polish Notation.
Example. a b – c +
Applications of Stacks … Cont’d
The following table briefly tries to show the difference in all three notations −
Sr.No. Infix Notation Prefix Notation Postfix Notation

1 a+b +ab ab+

2 (a + b) ∗ c ∗+abc ab+c∗

3 a ∗ (b + c) ∗a+bc abc+∗

4 a/b+c/d +/ab/cd ab/cd/+

5 (a + b) ∗ (c + d) ∗+ab+cd ab+cd+∗

6 ((a + b) ∗ c) - d -∗+abcd ab+c∗d-

Infix to Postfix conversion:


The compiler scans the expression either from left to right or from right to left. Consider the expression: a+b
*c+d
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+. The postfix expressions can be
evaluated easily using a stack.
Applications of Stacks … Cont’d
Algorithm
1. Scan the infix expression from left to right.
2. If the scanned character is an operand, output it.
3. Else,
a. 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.
b. 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.
Applications of Stacks … Cont’d
Applications of Stacks … Cont’d
Program to convert infix expression to postfix // If the scanned character is an ‘)’, pop and to output string from the stack until an ‘(‘ is
// Note that here we use std::stack for Stack operations encountered.
#include<bits/stdc++.h> else if(s[i] == ')')
using namespace std; {
//Function to return precedence of operators
while(st.top() != 'N' && st.top() != '(')
int prec(char c)
{ {
if(c == '^') char c = st.top();
return 3; st.pop();
else if(c == '*' || c == '/') ns += c;
return 2; }
else if(c == '+' || c == '-')
if(st.top() == '(')
return 1;
else {
return -1; char c = st.top();
} st.pop();
// The main function to convert infix expression to postfix expression }
void infixToPostfix(string s) }
{ //If an operator is scanned
std::stack<char> st; else{
st.push('N'); while(st.top() != 'N' && prec(s[i]) <= prec(st.top()))
int l = s.length();
{
string ns;
char c = st.top();
for(int i = 0; i < l; i++)
st.pop();
{
// If the scanned character is an operand, add it to output string. ns += c;
if((s[i] >= 'a' && s[i] <= 'z')||(s[i] >= 'A' && s[i] <= 'Z')) }
ns+=s[i]; st.push(s[i]);
}
// If the scanned character is an ‘(‘, push it to the stack. }
else if(s[i] == '(')
st.push('(');
Applications of Stacks … Cont’d
//Pop all the remaining elements from the stack
while(st.top() != 'N')
{
char c = st.top();
st.pop();
ns += c;
}
cout << ns << endl;
}
//Driver program to test above functions
int main()
{
string exp = "a+b*(c^d-e)^(f+g*h)-i";
infixToPostfix(exp);
return 0;
}

Output:
abcd^e-fgh*+^*+i-
Stacks … Cont’d
Recursion:
The process in which a function calls itself directly or indirectly is called recursion and the
corresponding function is called as recursive function.
In the recursive program, the solution to the base case is provided and the solution of the
bigger problem is expressed in terms of smaller problems.
int fact(int n)
{
if (n < = 1) // base case
return 1;
else
return n*fact(n-1);
}
In the above example, base case for n < = 1 is defined and larger value of number can be solved
by converting to smaller one till base case is reached.
Stacks … Cont’d
Properties of Recursion:
A recursive function can go infinite like a loop. To avoid infinite running of recursive function,
there are two properties that a recursive function must have
• Base criteria − There must be at least one base criteria or condition, such that, when this condition
is met the function stops calling itself recursively.
• Progressive approach − The recursive calls should progress in such a way that each time a recursive
call is made it comes closer to the base criteria.
Implementation:
Many programming languages implement recursion by means of stacks. Generally,
whenever a function (caller) calls another function (callee) or itself as callee, the caller function
transfers execution control to the callee. This transfer process may also involve some data to be
passed from the caller to the callee.
This implies, the caller function has to suspend its execution temporarily and resume later
when the execution control returns from the callee function. Here, the caller function needs to
start exactly from the point of execution where it puts itself on hold. It also needs the exact
same data values it was working on. For this purpose, an activation record (or stack frame) is
created for the caller function.
Stacks … Cont’d

This activation record keeps the information about local variables, formal parameters, return
address and all information passed to the caller function.
When any function is called from main(), the memory is allocated to it on the stack. A recursive
function calls itself, the memory for a called function is allocated on top of memory allocated to
calling function and different copy of local variables is created for each function call. When the
base case is reached, the function returns its value to the function by whom it is called and
memory is de-allocated and the process continues.
Let us take the example how recursion works by taking a simple function.
Stacks … Cont’d

//C++ program to demonstrate working of recursion When printFun(3) is called from main(), memory is allocated to
printFun(3) and a local variable test is initialized to 3 and
#include <bits/stdc++.h> statement 1 to 4 are pushed on the stack as shown in below
using namespace std; diagram. It first prints ‘3’.
In statement 2, printFun(2) is called and memory is allocated to
void printFun(int test)
printFun(2) and a local variable test is initialized to 2 and
{
statement 1 to 4 are pushed in the stack.
if (test < 1)
return; Similarly, printFun(2) calls printFun(1) and printFun(1) calls
else printFun(0). printFun(0) goes to if statement and it return to
{ printFun(1).
cout << test << " "; //statement 1
Remaining statements of printFun(1) are executed and it
printFun(test - 1); // statement 2
returns to printFun(2) and so on. In the output, value from 3 to
cout << test << " "; // statement 3
1 are printed and then 1 to 3 are printed. The memory stack
return; // statement 4
has been shown in below diagram.
}
}
int main()
{
int test = 3; Output:
printFun(test); 3 2 1 1 2 3
}
Stacks … Cont’d
Maze Problem ( Rat in a Maze):
A Maze is given as N*N binary matrix of blocks where source block is the upper left most
block i.e., maze[0][0] and destination block is lower rightmost block i.e., maze[N-1][N-1].
A rat starts from source and has to reach the destination. The rat can move only in two
directions: forward and down.
In the maze matrix, 0 means the block is a dead end and 1 means the block can be used in the
path from source to destination. Note that this is a simple version of the typical Maze problem.
For example, a more complex version can be that the rat can move in 4 directions and a more
complex version can be with a limited number of moves.
Following is an example maze.
Gray blocks are dead ends (value = 0).
Following is binary matrix representation of the maze.
Maze Problem… Cont’d
Following is a maze with highlighted solution path and the solution Matrix.(output of program)

Approach:
Form a recursive function, which will follow a path and check if the path reaches the destination or not. If the path does not reach
the destination then backtrack and try other paths.
Algorithm:
• Create a solution matrix, initially filled with 0’s.
• Create a recursive function, which takes initial matrix, output matrix and position of rat (i, j).
• if the position is out of the matrix or the position is not valid then return.
• Mark the position output[i][j] as 1 and check if the current position is destination or not. If destination is reached print the
output matrix and return.
• Recursively call for position (i+1, j) and (i, j+1).
• Unmark position (i, j), i.e output[i][j] = 0.
Queue
Queue is an abstract data structure, somewhat similar to Stacks. Unlike stacks, a queue is open at
both its ends. One end is always used to insert data (enqueue) and the other is used to remove data
(dequeue). Queue follows First-In-First-Out methodology, i.e., the data item stored first will be accessed
first.

A real-world example of queue can be a single-lane one-way road, where the vehicle enters first,
exits first. More real-world examples can be seen as queues at the ticket windows and bus-stops.
Queue Representation
As we now understand that in queue, we access both ends for different reasons. The following diagram
given below tries to explain queue representation as data structure

As in stacks, a queue can also be implemented using Arrays, Linked-lists, Pointers and Structures. For the
sake of simplicity, we shall implement queues using one-dimensional array.
Queue… Cont’d
Basic Operations
Queue operations may involve initializing or defining the queue, utilizing it, and then completely erasing it
from the memory. Here we shall try to understand the basic operations associated with queues
• enqueue() − add (store) an item to the queue.
• dequeue() − remove (access) an item from the queue.
Few more functions are required to make the above-mentioned queue operation efficient. These are
• peek() − Gets the element at the front of the queue without removing it.
• isfull() − Checks if the queue is full.
• isempty() − Checks if the queue is empty.
In queue, we always dequeue (or access) data, pointed by front pointer and while enqueuing (or storing)
data in the queue we take help of rear pointer.
Let's first learn about supportive functions of a queue
peek()
This function helps to see the data at the front of the queue. The algorithm of peek() function is as follows
Queue… Cont’d
isfull()
As we are using single dimension array to implement queue, we just check for the rear pointer to reach at
MAXSIZE to determine that the queue is full. In case we maintain the queue in a circular linked-list, the
algorithm will differ. Algorithm of isfull() function

isempty()
Algorithm of isempty() function
Queue… Cont’d
Enqueue Operation
Queues maintain two data pointers, front and rear. Therefore, its operations are comparatively difficult to implement
than that of stacks.
The following steps should be taken to enqueue (insert) data into a queue
• Step 1 − Check if the queue is full.
• Step 2 − If the queue is full, produce overflow error and exit.
• Step 3 − If the queue is not full, increment rear pointer to point the next empty space.
• Step 4 − Add data element to the queue location, where the rear is pointing.
• Step 5 − return success.

Sometimes, we also check to see if a queue is initialized or not, to handle any unforeseen situations
Queue… Cont’d

Implementation of enqueue()
Queue… Cont’d
Dequeue Operation
Accessing data from the queue is a process of two tasks − access the data where front is pointing and
remove the data after access.
The following steps should be taken to perform dequeue operation.
• Step 1 − Check if the queue is empty.
• Step 2 − If the queue is empty, produce underflow error and exit.
• Step 3 − If the queue is not empty, access the data where front is pointing.
• Step 4 − Increment front pointer to point to the next available data element.
• Step 5 − Return success.
Queue… Cont’d

Implementation of enqueue()
Queue… Cont’d

Applications of Queue
Queue, as the name suggests is used whenever we need to manage any group of objects in an order in
which the first one coming in, also gets out first while the others wait for their turn, like in the following
scenarios:
1. Serving requests on a single shared resource, like a printer, CPU task scheduling etc.
2. In real life scenario, Call Center phone systems uses Queues to hold people calling them in an
order, until a service representative is free.
3. Handling of interrupts in real-time systems. The interrupts are handled in the same order as
they arrive i.e First come first served.
Queue… Cont’d
Circular Queue
Circular Queue is also a linear data structure, which follows the principle of FIFO(First In First Out), but
instead of ending the queue at the last position, it again starts from the first position after the last, hence
making the queue behave like a circular data structure.
why we need a circular queue
In a Linear queue, once the queue is completely full, it's not possible to insert more elements. Even if we
dequeue the queue to remove some of the elements, until the queue is reset, no new elements can be inserted.

When we dequeue any element to remove it from the queue, we are actually moving the front of the queue
forward, thereby reducing the overall size of the queue. And we cannot insert new elements, because the rear
pointer is still at the end of the queue.

The only way is to reset the linear queue, for a fresh start.
Circular Queue… Cont’d
Basic features of Circular Queue
1. In case of a circular queue, head pointer will always point to the front of the queue, and tail
pointer will always point to the end of the queue.
2. Initially, the head and the tail pointers will be pointing to the same location, this would mean that
the queue is empty.

3. New data is always added to the location pointed by the tail pointer, and once the data is added,
tail pointer is incremented to point to the next available location.
Circular Queue… Cont’d
Basic features of Circular Queue
4. In a circular queue, data is not actually removed from the queue. Only the head pointer is
incremented by one position when dequeue is executed. As the queue data is only the data between
head and tail, hence the data left outside is not a part of the queue anymore, hence removed.

5. The head and the tail pointer will get reinitialised to 0 every time they reach the end of the queue.
Circular Queue… Cont’d
Basic features of Circular Queue
6. Also, the head and the tail pointers can cross each other. In other words, head pointer can be greater
than the tail. Sounds odd? This will happen when we dequeue the queue a couple of times and the tail
pointer gets reinitialized upon reaching the end of the queue.

Another very important point is keeping the value of the tail and the head pointer within the maximum
queue size. In the diagrams above the queue has a size of 8, hence, the value of tail and head pointers will
always be between 0 and 7.
This can be controlled either by checking every time whether tail or head have reached the maxSize and
then setting the value 0 or, we have a better way, which is, for a value x if we divide it by 8, the remainder
will never be greater than 8, it will always be between 0 and 0, which is exactly what we want.
So the formula to increment the head and tail pointers to make them go round and round over and again
will be, head = (head+1) % maxSize or tail = (tail+1) % maxSize
Linked List
A linked list is a sequence of data structures, which are connected together via links.
Linked List is a sequence of links which contains items. Each link contains a connection to another link.
Linked list is the second most-used data structure after array. Following are the important terms to
understand the concept of Linked List.
• Link − Each link of a linked list can store a data called an element.
• Next − Each link of a linked list contains a link to the next link called Next.
• LinkedList − A Linked List contains the connection link to the first link called First.

Linked List Representation


Linked list can be visualized as a chain of nodes, where every node points to the next node.

As per the above illustration, following are the important points to be considered.
• Linked List contains a link element called first.
• Each link carries a data field(s) and a link field called next.
• Each link is linked with its next link using its next link.
• Last link carries a link as null to mark the end of the list.
Linked List …Cont’d

Types of Linked List


Following are the various types of linked list
• Singly Linked List − Item navigation is forward only.
• Doubly Linked List − Items can be navigated forward and backward.
• Circular Linked List − Last item contains link of the first element as next and the first element has a
link to the last element as previous.
Singly Linked List
Basic Operations
Following are the basic operations supported by a list.
• Insertion − Adds an element at the beginning of the list.
• Deletion − Deletes an element at the beginning of the list.
• Display − Displays the complete list.
• Search − Searches an element using the given key.
• Delete − Deletes an element using the given key.
Linked List …Cont’d

Insertion Operation
Adding a new node in linked list is a more than one step activity. We shall learn this with diagrams here.
First, create a node using the same structure and find the location where it has to be inserted.

Imagine that we are inserting a node B (NewNode), between A (LeftNode) and C (RightNode). Then point
B.next to C
It should look like this
Linked List …Cont’d

Now, the next node at the left should point to the new node.

This will put the new node in the middle of the two. The new list should look like this

Similar steps should be taken if the node is being inserted at the beginning of the list. While inserting it at
the end, the second last node of the list should point to the new node and the new node will point to NULL.
Linked List …Cont’d

Deletion Operation
Deletion is also a more than one step process. We shall learn with pictorial representation. First, locate the
target node to be removed, by using searching algorithms.

The left (previous) node of the target node now should point to the next node of the target node

This will remove the link that was pointing to the target node. Now, using the following code, we will
remove what the target node is pointing at.
Linked List …Cont’d

We need to use the deleted node. We can keep that in memory otherwise we can simply deallocate
memory and wipe off the target node completely.

Reverse Operation:
This operation is a thorough one. We need to make the last node to be pointed by the head node and
reverse the whole linked list.

First, we traverse to the end of the list. It should be pointing to NULL. Now, we shall make it point to its
previous node
Linked List …Cont’d

We have to make sure that the last node is not the last node. So we'll have some temp node, which
looks like the head node pointing to the last node. Now, we shall make all left side nodes point to their
previous nodes one by one.

Except the node (first node) pointed by the head node, all nodes should point to their predecessor, making
them their new successor. The first node will point to NULL.

We'll make the head node point to the new first node by using the temp node.

The linked list is now reversed.


Linked List …Cont’d

Representation of a Polynomial
A polynomial p(x) is the expression in variable x which is in the form (ax^n + bx^n-1 + …. + jx+ k),
where a, b, c …., k fall in the category of real numbers and 'n' is non negative integer, which is called
the degree of polynomial.
An essential characteristic of the polynomial is that each term in the polynomial expression consists of
two parts
1. Coefficient
2. Exponent
Example: 10x^2 + 26x, here 10 and 26 are coefficients and 2, 1 is its exponential value.
Representation of a Polynomial…Cont’d
Points to keep in Mind while working with Polynomials:
• The sign of each coefficient and exponent is stored within the coefficient and the exponent itself
• Additional terms having equal exponent is possible one
• The storage allocation for each term in the polynomial must be done in ascending and descending
order of their exponent
Adding two polynomials using Linked List
Given two polynomial numbers represented by a linked list. Write a function that add these lists means add
the coefficients who have same variable powers.
Representation of a Polynomial…Cont’d
Doubly Linked List
Doubly Linked List is a variation of Linked list in which navigation is possible in both ways,
either forward and backward easily as compared to Single Linked List. Following are the
important terms to understand the concept of doubly linked list.

• Link − Each link of a linked list can store a data called an element.
• Next − Each link of a linked list contains a link to the next link called Next.
• Prev − Each link of a linked list contains a link to the previous link called Prev.
• LinkedList − A Linked List contains the connection link to the first link called First and to the
last link called Last.
Doubly Linked List Representation
Doubly Linked List…Cont’d
As per the above illustration, following are the important points to be considered.
• Doubly Linked List contains a link element called first and last.
• Each link carries a data field(s) and two link fields called next and prev.
• Each link is linked with its next link using its next link.
• Each link is linked with its previous link using its previous link.
• The last link carries a link as null to mark the end of the list.
Basic Operations:
Following are the basic operations supported by a list.
• Insertion − Adds an element at the beginning of the list.
• Deletion − Deletes an element at the beginning of the list.
• Insert Last − Adds an element at the end of the list.
• Delete Last − Deletes an element from the end of the list.
• Insert After − Adds an element after an item of the list.
• Delete − Deletes an element from the list using the key.
• Display forward − Displays the complete list in a forward manner.
• Display backward − Displays the complete list in a backward manner.
Doubly Linked List…Cont’d
Insertion Operation
Following code demonstrates the insertion operation at the beginning of a doubly linked list.
Doubly Linked List…Cont’d
Deletion Operation
Following code demonstrates the deletion operation at the beginning of a doubly linked list.
Doubly Linked List…Cont’d
Insertion at the End of an Operation
Following code demonstrates the insertion operation at the last position of a doubly linked list.
Doubly Linked List…Cont’d

Applications of Doubly Linked List:

You might also like