Unit I
Unit I
UNIT-1:
Data Structure:
It is a particular way of organizing and storing data in a computer. So that it can be
accessed and modified efficiently. A data structure will have a collection of data and
functions or operations that can be applied on the data.
Non-Primitive Data Structures are that defines a set of derived elements such as Arrays,
Structures and Classes.
Non-Linear Data Structures are used to represent data that have a hierarchical relationship
among the elements. In non-linear Data Structure every element has more than one
predecessor and one successor.
E.g.:- Array
Data Structures that are created at run time are called as Dynamic Data Structure. The
variables of this type are known always referred by user defined name instead using their
addresses through pointers.
Direct Access means any element can access directly without accessing its predecessor or
successor i.e., 𝒏𝒕𝒉 element can be accessed directly.
E.g.:- Array
COMPLEXITY OF ALGORITHMS
Generally algorithms are measured in terms of time complexity and space complexity.
1. Time Complexity
Time Complexity of an algorithm is a measure of how much time is required to
execute an algorithm for a given number of inputs. And it is measured by its rate of growth
relative to a standard function. Time Complexity can be calculated by adding compilation
time and execution time. Or it can do by counting the number of steps in an algorithm.
2. Space Complexity
Space Complexity of an algorithm is a measure of how much storage is required by
the algorithm. Thus space complexity is the amount of computer memory required during
program execution as a function of input elements. The space requirement of algorithm can
be performed at compilation time and run time.
ASYMPTOTIC ANALYSIS
For analysis of algorithm it needs to calculate the complexity of algorithms in terms
of resources, such as time and space. But when complexity is calculated, it does not provide
2
the exact amount of resources required. Therefore instead of taking exact amount of
resources, the complexity of algorithm is represented in a general mathematical form which
will give the basic nature of algorithm.
ASYMPTOTIC NOTATION
The commonly used Asymptotic Notations to calculate the complexity of algorithms are:
1. Big Oh – O: - The notation O(𝒏) is the formal way to express the upper bound of an
algorithm’s complexity.
2. Big Omega – Ω: - The notation Ω(𝒏) is the formal way of representing lower bound of
an algorithm’s complexity.
3
Ω (g(n)) = {f(n): there exist positive constants c and
n0 such that 0 <= c*g(n) <= f(n) for
all n >= n0}.
3. Big Theta – Ө: - The notation Ө(𝒏) is the formal way to representing lower bound and
upper bound of an algorithm’s complexity.
STACK
Stack is a linear Data Structure in which all the insertion and deletion operations are
done at one end, called top of the stack and operations are performed in Last In First Out
[LIFO] order. A stack can be implemented by using an array or by using linked list.
The array representation method needs to set the amount memory needed initially.
So they are limited in size, i.e. it can’t shrink or expand. Linked List representation uses
Dynamic memory management method. So they are not limited in size i.e. they can shrink
or expand.
class stack
{
int a[10], st;
public:
stack()
{
st =-1;
}
void push(int);
4
void pop();
};
5
The operations performed on Stack are:
1. Push Operation: This function is used to store data into the stack. The Push
operation performs following steps:
i. Check whether stack is full or not.
ii. If full, message that stack is full.
iii. If not full, it will increase the stack top by 1 and to that location new data is
stored.
6
Implement Stack ADT using array:
//Template class for Stack
#include<iostream>
template<class T>
class stack
int size,top;
T *a;
public:
stack(int);
int isEmpty();
int isFull();
void push();
void pop();
};
template<class T>
stack<T>::stack(int s)
size=s;
a= new T[size];
top=-1;
7
template<class T>
int stack<T>::isEmpty()
if(top==-1)
return 1;
else
return 0;
template<class T>
int stack<T>::isFull()
if(top==size-1)
return 1;
else
return 0;
template<class T>
void stack<T>::push()
if(isFull())
cout<<"Stack is Full"<<endl;
else
int item;
cin>>item;
top++;
a[top]=item;
}}
8
template<class T>
void stack<T>::pop()
if(isEmpty())
cout<<"Stack is Empty"<<endl;
else
T x;
x=a[top];
top--;
cout<<"Data= "<<x<<endl;
}}
main()
stack<int>S(3);
int y=1,op,t;
while(y==1)
cin>>op;
switch(op)
case 1: S.push();
break;
case 2: S.pop();
break;
case 3: y=0;
break;
}}
return 0; }
9
APPLICATIONS OF STACK
POLISH NOTATIONS
This gives two alternatives to represent an arithmetic expression called postfix and
prefix notations. The fundamental property of polish notation is that the order in which the
operations are to be performed is determined by positions of operators and operands in the
expression. Hence the advantage is that parenthesis is not required while writing
expressions in polish notations. It also overrides associatively of operators. The conventional
way of writing expression is called infix because in binary operations the operators occur
between the operands. In postfix notation the operator is written after its operands.
E.g.: AB+
E.g.: +AB
10
Algorithm for evaluating Postfix expression
1) Start
2) Let E denote postfix expression.
3) Let stacktop=-1.
4) While(1) do
Begin x=getnext(E)
If(x==#)
Then return
If x is an operand, then push(x) otherwise
Begin
op2=pop,
op1=pop,
op3=evaluate(op1,x,op2)
Push(op3)
End
End while
5) Stop.
11
5) If(ch==#)
Then while(!isempty())
ch=pop()
display ch
end while
6) Stop.
#include<iostream>
#include<string.h>
template<class T>
class stack
int size,top;
T *a;
public:
stack(int);
int isEmpty();
int isFull();
void push(T);
T pop();
T getNext(T*,int);
int isOperator(T);
int ICP(T);
int ISP(T);
};
template<class T>
return w[i];
}
12
template<class T>
int stack<T>::isOperator(T x)
int i;
if(x=='+'||x=='-'||x=='*'||x=='/'||x=='^'||x=='(')
return 1;
else
return 0;
template<class T>
int stack<T>::ISP(T y)
if(y=='^')
return 3;
else if(y=='*'||y=='/')
return 2;
else if(y=='+'||y=='-')
return 1;
else if(y=='(')
return 0;
template<class T>
int stack<T>::ICP(T z)
if(z=='^'||z=='(')
return 4;
else if(z=='*'||z=='/')
return 2;
else if(z=='+'||z=='-')
return 1;
}
13
template<class T>
stack<T>::stack(int s)
size=s;
a= new T[size];
top=-1;
template<class T>
int stack<T>::isEmpty()
if(top==-1)
return 1;
else
return 0;
template<class T>
int stack<T>::isFull()
if(top==size-1)
return 1;
else
return 0;
template<class T>
if(!isFull()) a[+
+top]=item;
14
template<class T>
T stack<T>::pop()
if(!isEmpty())
return a[top--];
main()
stack<char>S(10);
char c[20],ch,t;
int i=0,l;
cin>>c;
ch=S.getNext(c,i++);
while(ch !='#')
if(ch==')')
ch=S.pop();
while(ch != '(')
cout<<ch;
ch=S.pop();
else if(S.isOperator(ch))
t=S.pop();
if(S.ICP(ch)>S.ISP(t))
S.push(t);
15
S.push(ch);
else
while(S.ICP(ch)<=S.ISP(t))
cout<<t;
t=S.pop();
S.push(t);
S.push(ch);
else
cout<<ch;
ch=S.getNext(c,i++);
while(!S.isEmpty())
cout<<S.pop();
cout<<endl;
return 0;
16
Q. Implement Post fix Evaluation
#include<iostream>
#include<string.h>
template<class T>
class stack
int size,top;
T *a;
public:
stack(int);
int isEmpty();
int isFull();
void push(T);
T pop();
T getNext(T*,int);
int isOperator(T);
T eval(T,T,T);
};
template<class T>
T t;
switch(b)
case'+': t=a+c;
return t;
break;
case'-': t=a-c;
return t;
17
break;
case'*': t=a*c;
return t;
break;
case'/': t=a/c;
return t;
}}
template<class T>
return w[i];
template<class T>
int stack<T>::isOperator(T x)
int i;
if(x=='+'||x=='-'||x=='*'||x=='/'||x=='^'||x=='(')
return 1;
else
return 0;
template<class T>
stack<T>::stack(int s)
size=s;
a= new T[size];
top=-1;
template<class T>
int stack<T>::isEmpty()
{
18
if(top==-1)
return 1;
else
return 0;
template<class T>
int stack<T>::isFull()
if(top==size-1)
return 1;
else
return 0;
template<class T>
if(!isFull()) a[+
+top]=item;
template<class T>
T stack<T>::pop()
if(!isEmpty())
return a[top--];
main()
stack<char>S(10);
char c[20],ch,op1,op2,op3;
int i=0;
ch=S.getNext(c,i++);
while(ch !='#')
if(S.isOperator(ch))
op2=S.pop();
op1=S.pop();
op3=S.eval(op1,ch,op2);
S.push(op3);
else
ch=ch-'0';
S.push(ch);
ch=S.getNext(c,i++);
if(ch=='#')
op3=S.pop();
op3=op3+'0';
cout<<op3<<endl;
return 0;
QUEUE
It’s a linear Data Structure in which insertion is done at rear end and deletion at front
end. The operations are performed in First In First Out order [FIFO]. This can be
implemented by using array or by linked list. The array representation uses static memory
allocation method, so they are limited in size i.e. they cannot shrink or expand. The linked
20
list representation uses dynamic memory management method, so they can shrink or
expand.
class queue
{
int a[10], f, r;
public:
queue()
{
f=-1;
r=-1;
}
void enque(int);
void deque();
};
1. Insertion or Enqueue: This function is used to store data into the Queue. The
operation perform the following steps:
i. Check whether Queue is full or not.
ii. If full, print a message that queue is full.
iii. If not full, then increment rear by 1 and to that location new data is inserted.
2. Deletion or Dequeue: This function is used to remove data from the Queue. The
Dqueue function performs the following steps:
i. Check whether Queue is empty or not.
ii. If empty, message that Queue is empty.
iii. If not empty, print the element represented by the front location and increment
the front by 1.
21
void queue :: dequeue
{
if((f==-1) || (front>rear))
cout<<"Queue is empty";
else
cout<<a[f++];
}
#include<iostream>
template<class T>
class queue
int size,f,r;
T *a;
public:
queue(int);
int isEmpty();
int isFull();
void enqueue(T);
void dequeue();
};
template<class T>
queue<T>::queue(int s)
size=s;
a=new T[size];
f=-1;
r=-1;
};
template<class T>
22
int queue<T>::isFull()
if(r==size-1)
return 1;
else
return 0;
template<class T>
int queue<T>::isEmpty()
if(f==r)
return 1;
else
return 0;
template<class T>
void queue<T>::enqueue(T x)
if(isFull())
cout<<"Queue is Full"<<endl;
else
a[++r]=x;
template<class T>
void queue<T>::dequeue()
if(isEmpty())
cout<<"Queue is Empty"<<endl;
else
cout<<a[++f]<<endl;
}
23
int main()
queue<int> Q(5);
int y=1,op,t;
while(y==1)
cin>>op;
switch(op)
cin>>t;
Q.enqueue(t);
break;
case 2: Q.dequeue();
break;
case 3: y=0;
}}
return 0;
CIRCULAR QUEUE
It is a linear Data Structure in which operations are performed in FIFO order and last
position is connected back to the first position to make a circle. A Circular Queue is also
called as ring buffer. In a normal Queue it is possible to insert elements until queue
becomes full, but once queue become full, it is impossible to add next data even if there is a
space in front of queue. This situation can be overridden in Circular Queue.
24
Operations on Circular Queue
1. Enqueue: This function is used to insert an element to the circular queue. A new data
is inserted at the rear position. Steps followed for an Enqueue:
i. Check whether Queue is full or not.
ii. If queue is full, display message that queue is full.
iii. If not full, front is set to zero, rear end is moded with the circular queue size after
rear is incremented by 1 and then add the element to the location.
2. Dequeue: This function is used to delete an element from the circular queue. In
circular queue elements are always deleted from different positions. Steps followed for
a Dequeue are:
i. Check whether Queue is empty.
ii. If empty, give a message queue is empty.
iii. If not empty, the element at that location is displayed. Then front end and rear end
is set to -1 if front is equal to rear otherwise front is moded by the queue size after
front is incremented by 1.
f=-1;
} r=-1;
else
}} f=(f+1)%s;
25
Implement Circular Queue ADT:
//Circular Queue implementation
#include<iostream>
template<class T>
class queue
int size,f,r;
T *a;
public:
queue(int);
int isEmpty();
int isFull();
void enqueue(T);
void dequeue();
};
template<class T>
queue<T>::queue(int s)
size=s;
a=new T[size];
f=-1;
r=-1;
template<class T>
int queue<T>::isEmpty()
if(f>r||f==-1)
26
return 1;
else
return 0;
template<class T>
int queue<T>::isFull()
if((r+1)%size==f)
return 1;
else
return 0;
template<class T>
void queue<T>::enqueue(T x)
if(isFull())
cout<<"Queue is Full"<<endl;
else
if(f==-1)
{ f=
0;
r=0;
a[r]=x;
else
r=(r+1)%size;
27
a[r]=x;
}}}
template<class T>
void queue<T>::dequeue()
if(isEmpty())
cout<<"Queue is Empty"<<endl;
else
cout<<a[f]<<endl;
if(f==r)
f=-1;
r=-1;
else
f=(f+1)%size;
}}
int main()
queue<int> Q(5);
int y=1,op,t;
while(y==1)
cin>>op;
switch(op)
28
case 1: cout<<"Enter the data"<<endl;
cin>>t;
Q.enqueue(t);
break;
case 2: Q.dequeue();
break;
case 3: y=0;
}}
return 0;
Variations of Dequeue
1. Input restricted Double Ended Queue: - These types of Double ended queues allow
insertions only at one end.
29
2. Output restricted Double Ended Queue: - These types of Double ended queues allow
deletion from only one end.
PRIORITY QUEUES
A priority Queue is a collection of a finite number of prioritised elements. Elements can be
inserted in any order in the priority queue but when the element is removed, it is always the
element with highest priority.
1. Element with highest priority is processed before any element of lower priority.
2. If there where elements with same priority, elements added first in the queue would get
processed first.
1. Implement separate queues for each priority: - In this case elements are removed from
front of the queue. Elements of the second queue are removed only when the first queue
is empty. And elements from third queue are removed only when the second queue is
empty.
2. Implement by using a Structure or by a Class for the queue: -Here each element in the
queue has a data part and priority part of the element. The highest priority element is
stored at the front of the queue and lowest priority element at the rear.
30