Professional Documents
Culture Documents
DSA Unit 1
DSA Unit 1
MITRC
1
Unit-1 CSE- III SEM DSA
UNIT-1
Definition of Algorithms:
It describes:
Ex:
ATN( )
Properties of algorithms:
2
Unit-1 CSE- III SEM DSA
2. Design the algorithm.
3. Draw flow chart.
4. Verification and testing.
5. Coding and implementation.
6. Analysis or analysing algorithm.
Analysis of algorithm:
If the problem contain more than one solution we will find the better solution based on two
parameter.
1. Time
2. Space
Before defining abstract data types, let us consider the different view of system-defined data
types. We all know that, by default, all primitive data types (int, float, etc.) support basic
operations such as addition and subtraction. The system provides the implementations for the
primitive data types. For user-defined data types we also need to define operations. The
implementation for these operations can be done when we want to actually use them. That
means, in general, user defined data types are defined along with their operations. To simplify
the process of solving problems, we combine the data structures with their operations and we call
this Abstract Data Types (ADTs). An ADT consists of two parts:
1. Declaration of data
2. Declaration of operations
Commonly used ADTs include: Linked Lists, Stacks, Queues, Priority Queues, Binary Trees,
Dictionaries, Disjoint Sets (Union and Find), Hash Tables, Graphs, and many others.
For example, stack uses LIFO (Last-In-First-Out) mechanism while storing the data in data
structures. The last element inserted into the stack is the first element that gets deleted. Common
operations of it are: creating the stack, pushing an element onto the stack, popping an element
from stack, finding the current top of the stack, finding number of elements in the stack etc.
3
Unit-1 CSE- III SEM DSA
Types of Analysis
To analyze the given algorithm, we need to know with which inputs the algorithm takes less time
(performing wel1) and with which inputs the algorithm takes a long time. We have already seen
that an algorithm can be represented in the form of an expression. That means we represent the
algorithm with multiple expressions: one for the case where it takes less time and another for the
case where it takes more time.
In general, the first case is called the best case and the second case is called the worst case for
the algorithm. To analyze an algorithm we need some kind of syntax, and that forms the base for
asymptotic analysis/notation. There are three types of analysis:
Worst case
1. Defines the input for which the algorithm takes a long time (slowest time to
complete).
2. Input is the one for which the algorithm runs the slowest.
MITRC
Best case
1. Defines the input for which the algorithm takes the least time (fastest time to
complete).
2. Input is the one for which the algorithm runs the fastest.
Average case
1. Provides a prediction about the running time of the algorithm.
2. Run the algorithm many times, using many different inputs that come from some
distribution that generates these inputs, compute the total running time (by adding the
individual times), and divide by the number of trials.
3. Assumes that the input is random.
For a given algorithm, we can represent the best, worst and average cases in the form of
expressions. As an example, let f(n) be the function which represents the given algorithm.
Similarly for the average case. The expression defines the inputs with which the algorithm takes
the average running time (or memory).
4
Unit-1 CSE- III SEM DSA
LECTURE NO. 2
Asymptotic Notation
Having the expressions for the best, average and worst cases, for all three cases we need to
identify the upper and lower bounds. To represent these upper and lower bounds, we need some
kind of syntax, and that is the subject of the following discussion. Let us assume that the given
algorithm is represented in the form of function f(n).
5
Unit-1 CSE- III SEM DSA
∴ 2n3 – 2n2 = O(n3 ) with c = 2 and n0 = 1
Note 1. If both function are greater by fraction of a no. of function, it cannot handle it.
Similar to the O discussion, this notation gives the tighter lower bound of the given algorithm
and we represent it as f(n) = Ω(g(n)). That means, at larger values of n, the tighter lower bound
of f(n) is g(n). For example, if f(n) = 100n2 + 10n + 50, g(n) is Ω(n2).
Ω Examples
6
Unit-1 CSE- III SEM DSA
Since n is positive ⇒cn - 105 ≤0 ⇒n ≤105/c
Θ notation
Now consider the definition of Θ notation. It is defined as Θ(g(n)) = {f(n): there exist positive
constants c1,c2 and n0 such that 0 ≤c1g(n) ≤ f(n) ≤ c2g(n) for all n ≥n0}. g(n) is an asymptotic
tight bound for f(n). Θ(g(n)) is the set of functions with the same order of growth as g(n).
Examples: MITRC
∴n ≠Θ(n2)
∴ 6n3 ≠Θ(n2)
7
Unit-1 CSE- III SEM DSA
Important Notes:
For analysis (best case, worst case and average), we try to give the upper bound (O) and lower
bound (Ω) and average running time (Θ). From the above examples, it should also be clear that,
for a given function (algorithm), getting the upper bound (O) and lower bound (Ω) and average
running time (Θ) may not always be possible. For example, if we are discussing the best case of
an algorithm, we try to give the upper bound (O) and lower bound (Ω) and average running time
(Θ).
MITRC
LECTURE NO.3
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).
Definition:A stack is an ordered list in which insertion and deletion are done at one end, called top. The
last element inserted is the first one to be deleted. Hence, it is called the Last in First out (LIFO) or First
in Last out (FILO) list.
8
Unit-1 CSE- III SEM DSA
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 −
peek() − get the top data element of the stack, without removing it.
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.
Exceptions
Attempting the execution of an operation may sometimes cause an error condition, called an
exception. Exceptions are said to be “thrown” by an operation that cannot be executed. In the
Stack ADT, operations pop and top cannot be performed if the stack is empty. Attempting the
execution of pop (top) on an empty stack throws an exception. Trying to push an element in a
full stack throws an exception.
Applications of stack:
Following are some of the applications in which stacks play an important role.
Direct applications
• Balancing of symbols
• Infix-to-postfix conversion
9
Unit-1 CSE- III SEM DSA
Indirect applications
• Auxiliary data structure for other algorithms (Example: Tree traversal algorithms)
• Component of other data structures (Example: Simulating queues refer Queues chapter)
Implementation
There are many ways of implementing stack ADT; below are the commonly used methods.
• Simple array based implementation
• Dynamic array based implementation
• Linked lists implementation
Simple Array Implementation
This implementation of stack ADT uses an array. In the array, we add elements from left to right and use
a variable to keep track of the index of the top element. The array storing the stack elements may
become full. A push operation will then throw a fullstack exception. Similarly, if we try deleting an
MITRC
element from an empty stack it will throw stackempty exception.
Performance
Let n be the number of elements in the stack. The complexities of stack operations with this
10
Unit-1 CSE- III SEM DSA
Time Complexity of DeleteStackQ O(1)
Limitations
The maximum size of the stack must first be defined and it cannot be changed. Trying to push a new
element into a full stack causes an implementation-specific exception.
First, let’s consider how we implemented a simple array based stack. We took one index variable top
which points to the index of the most recently inserted element in the stack. To insert (or push) an
element, we increment top index and then place the new element at that index. Similarly, to delete (or
pop) an element we take the element at top index and then decrement the top index. We represent an
empty queue with top value equal to –1. The issue that still needs to be resolved is what we do when all
the slots in the fixed size array stack are occupied?
First try: What if we increment the size of the array by 1 every time the stack is full?
MITRC
• Push(); increase size of S[] by 1
This way of incrementing the array size is too expensive. Let us see the reason for this. For example, at n
= 1, to push an element create a new array of size 2 and copy all the old array elements to the new array,
and at the end add the new element. At n = 2, to push an element create a new array of size 3 and copy
all the old array elements to the new array, and at the end add the new element.
Similarly, at n = n – 1, if we want to push an element create a new array of size n and copy all the old
array elements to the new array and at the end add the new element. After n push operations the total
time T(n) (number of copy operations) is proportional to 1 + 2 + ... + n ≈ O(n2).
Let us improve the complexity by using the array doubling technique. If the array is full, create a new
array of twice the size, and copy the items. With this approach, pushing n items takes time proportional
to n (not n2). For simplicity, let us assume that initially we started with n = 1 and moved up to n = 32.
That means, we do the doubling at 1,2,4,8,16. The other way of analyzing the same approach is:
at n = 1, if we want to add (push) an element, double the current size of the array and copy all the
elements of the old array to the new array.
11
Unit-1 CSE- III SEM DSA
At n = 1, we do 1 copy operation, at n = 2, we do 2 copy operations, and at n = 4, we do 4 copy
operations and so on. By the time we reach n = 32, the total number of copy operations is 1+2 + 4
+ 8+16 = 31 which is approximately equal to 2n value (32). If we observe carefully, we are doing the
doubling operation logn times. Now, let us generalize the discussion. For n push operations we double
the array size logn times. That means, we will have logn terms in the expression below. The total time
T(n) of a series of n push operations is proportional to T(n) is O(n) and the amortized time of a push
operation is O(1) .
Performance
Let n be the number of elements in the stack. The complexities for operations with this
12
Unit-1 CSE- III SEM DSA
MITRC
LECTURE NO.4
The other way of implementing stacks is by using Linked lists. Push operation is implemented
by inserting element at the beginning of the list. Pop operation is implemented by deleting the
node from the beginning (the header/top node).
Performance
13
Unit-1 CSE- III SEM DSA
Let n be the number of elements in the stack. The complexities for operations with this
representation can be given as:
Comparison of Implementations
MITRC
Comparing Incremental Strategy and Doubling Strategy
We compare the incremental strategy and doubling strategy by analyzing the total time T(n)
needed to perform a series of n push operations. We start with an empty stack represented by an
array of size 1.
We call amortized time of a push operation is the average time taken by a push over the series
of operations, that is, T(n)/n.
Incremental Strategy
The amortized time (average time per operation) of a push operation is O(n) [O(n2)/n].
Doubling Strategy
Array Implementation
14
Unit-1 CSE- III SEM DSA
• Any sequence of n operations (starting from empty stack) –“amortized”bound takes time
proportional to n.
• Every operation uses extra space and time to deal with references.
MITRC
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 −
15
Unit-1 CSE- III SEM DSA
MITRC
LECTURE NO.5
16
Unit-1 CSE- III SEM DSA
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 de-allocates memory space.
Step 3 − If the stack is not empty, accesses the data element at which top is pointing.
else
{
17
Unit-1 CSE- III SEM DSA
TOP --;
S[TOP] = X;
}
}
1.) The number of element in an array is fixed where as for a stack there is no any bound.
2.) We are access any element in a array while its index value but for a stack only the top
element on be access. So we can not call an array a stack. But we can use array as a stack.
The problem with this method is inefficient use of array space. A stack push operation may result
in stack overflow even if there is space available in arr[]. For example, say the k is 2 and array
size (n) is 6 and we push 3 elements to first and do not push anything to second second stack.
When we push 4th element to first, there will be overflow even if we have space for 3 more
elements in array.
The idea is to use two extra arrays for efficient implementation of k stacks in an array. This may not make
much sense for integer stacks, but stack items can be large for example stacks of employees, students, etc
where every item is of hundreds of bytes. For such large stacks, the extra space used is comparatively
very less as we use two integer arrays as extra space.
18
Unit-1 CSE- III SEM DSA
Together with k stacks, a stack of free slots in arr[] is also maintained. The top of this stack is stored in a
variable ‘free’.
Application of Stack:
Reversing a List:
There are some steps for this task and these are:
1. Read a string.
2. Push all characters until NULL is
not found - Characters will be
stored in stack variable.
3. Pop all characters until NULL is
not found - As we know stack is
a LIFO technique, so last character
will be pushed first and finally we
will get reversed string in a variable
in which we store inputted string.
Algorithm:
void Reverse (char *c, int n)
{
Stack<char> S;
//Loop for Push
19
Unit-1 CSE- III SEM DSA
for (int i=0; i<n; i++)
{
S.PUSH (c[i]);
}
//Loop for POP
for (int i=0; i<n; i++)
{
c[i] = S.POP();
S.POP();
}
}
Factorial Calculation:
MITRC
It is recursive because it can be expresses recursively.
n! = f(n) = n f(n-1)
Program :-
#include<stdio.h>
#include<string.h>
#define MAX 100
// push method
int push(int *s, int top, int ele)
{
int i;
if (top>= MAX)
{
printf("\nStack Overflow");
}
else
{
s[++top] = ele;
}
20
Unit-1 CSE- III SEM DSA
return top;
}
//pop method
int pop(int *a, int *top)
{
if ((*top) >= 0)
{
(*top) = (*top) - 1;
return a[(*top) + 1];
}
else
{
printf("Stack underflow\n");
return 0;
} MITRC
}
void main()
{
int n;
int i; // loop variable
int ans = 1 ; // stroes the final answer
int TOP = -1; // stack variables that mainntain stack's top
int s[MAX]; // the stack
printf("\nEnter number: ");
scanf("%d",&n);
// here we can also make sure that the use is not entering a number
// that can not be accomodated in the stack
// if the user enters a number less than or equal to 0
// then we can not find factorial
if(n<=0)
{
printf("\nThe number can not be less than 0");
}
else
{
// push the numbers n,n-1 ....1 in the stack
21
Unit-1 CSE- III SEM DSA
// you can also go in reverse manner here. the result will be the same
// because in multiplication, order does not matter
for(i = n ; i>0 ; i--)
{
TOP = push(s,TOP, i);
}
// now pop all the elements one by one
// multiply them with the answer variable
while(TOP>=0)
{
ans = ans * pop(s,&TOP);
}
printf("\nFactorail is %d\n",ans);
}
// getch(); MITRC
LECTURE NO.6
Infix Notation
Prefix (Polish) Notation
These notations are named as how they use operator in expression. We shall learn the same here in this
chapter.
Infix Notation
22
Unit-1 CSE- III SEM DSA
We write expression in infix notation, e.g. a - b + c, where 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.
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.
Postfix Notation
This notation style is known as Reversed Polish Notation. In this notation style, 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.
MITRC
The following table briefly tries to show the difference in all three notations −
2 (a + b) ∗ c ∗+abc ab+c∗
3 a ∗ (b + c) ∗a+bc abc+∗
5 (a + b) ∗ (c + d) ∗+ab+cd ab+cd+∗
23
Unit-1 CSE- III SEM DSA
MITRC
LECTURE NO.7
24
Unit-1 CSE- III SEM DSA
6. If a right parenthesis is encountered ,then:
1. Repeatedly pop from Stack and add to Y each operator (on the top of Stack) until a left
parenthesis is encountered.
2. Remove the left Parenthesis.
[End of If]
[End of If]
7. END.
MITRC
25
Unit-1 CSE- III SEM DSA
LECTURE NO.8
Tower of Hanoi:
MITRC
The Tower of Honoi is also called the Tower of Brahma or Lucas tower.
It consists of 3 rules, and the number of disks of different sizes which can slide onto any rod. The
puzzle starts with the disks in a neat stack in ascending order of size on one rod, the smallest at
the top, thus make a conical shape.
The Objective o the puzzle is to move the entire stack to another rod obeying the following rule.
TOH (n-1, L, M)
MOVE (L-R)
TOH (n-1, M, L , R)
26
Unit-1 CSE- III SEM DSA
NOTE :
27
Unit-1 CSE- III SEM DSA
MITRC
28