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

unit 1 cad

Data structures are essential for efficiently storing and organizing data in computer science, with examples including arrays, linked lists, stacks, and queues. They enhance program performance by enabling quick data retrieval and manipulation, which is crucial as data complexity and volume increase. The document also covers the classification of data structures, operations performed on them, and the analysis of algorithms related to their efficiency.

Uploaded by

Manjunath 221 Cm
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
17 views

unit 1 cad

Data structures are essential for efficiently storing and organizing data in computer science, with examples including arrays, linked lists, stacks, and queues. They enhance program performance by enabling quick data retrieval and manipulation, which is crucial as data complexity and volume increase. The document also covers the classification of data structures, operations performed on them, and the analysis of algorithms related to their efficiency.

Uploaded by

Manjunath 221 Cm
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 28

Data Structures Page: 1

Data Structure
Data Structure can be defined as the group of data elements which provides an efficient way of storing
and organizing data in the computer so that it can be used efficiently. Some examples of Data
Structures are arrays, Linked List, Stack, Queue, etc. Data Structures are widely used in almost every
aspect of Computer Science i.e. Operating System, Compiler Design, Artifical intelligence, Graphics
and many more.
Data Structures are the main part of many computer science algorithms as they enable the
programmers to handle the data in an efficient way. It plays a vitle role in enhancing the performance
of a software or a program as the main function of the software is to store and retrieve the user's data
as fast as possible

Following terms are the foundation terms of a data structure.

• Interface − Each data structure has an interface. Interface represents the set of operations
that a data structure supports. An interface only provides the list of supported operations, type
of parameters they can accept and return type of these operations.
• Implementation − Implementation provides the internal representation of a data structure.
Implementation also provides the definition of the algorithms used in the operations of the
data structure.

Need of Data Structures

As applications are getting complexed and amount of data is increasing day by day, there may arrise
the following problems:
Processor speed: To handle very large amout of data, high speed processing is required, but as the
data is growing day by day to the billions of files per entity, processor may fail to deal with that much
amount of data.
Data Search: Consider an inventory size of 106 items in a store, If our application needs to search
for a particular item, it needs to traverse 106 items every time, results in slowing down the search
process.
Multiple requests: If thousands of users are searching the data simultaneously on a web server, then
there are the chances that a very large server can be failed during that process
in order to solve the above problems, data structures are used. Data is organized to form a data
structure in such a way that all items are not required to be searched and required data can be searched
instantly.

Advantages of Data Structures

Efficiency: Efficiency of a program depends upon the choice of data structures. For example: suppose,
we have some data and we need to perform the search for a perticular record. In that case, if we

B V Raju Institute of Technology


Data Structures Page: 2
organize our data in an array, we will have to search sequentially element by element. hence, using
array may not be very efficient here. There are better data structures which can make the search
process efficient like ordered array, binary search tree or hash tables.
Reusability: Data structures are reusable, i.e. once we have implemented a particular data structure,
we can use it at any other place. Implementation of data structures can be compiled into libraries
which can be used by different clients.
Abstraction: Data structure is specified by the ADT which provides a level of abstraction. The client
program uses the data structure through interface only, without getting into the implementation
details.

Data Structure Classification

Linear Data Structures: A data structure is called linear if all of its elements are arranged in the
linear order. In linear data structures, the elements are stored in non-hierarchical way where each
element has the successors and predecessors except the first and last element.
Types of Linear Data Structures are given below:
Arrays: An array is a collection of similar type of data items and each data item is called an element
of the array. The data type of the element may be any valid data type like char, int, float or double.
The elements of array share the same variable name but each one carries a different index number
known as subscript. The array can be one dimensional, two dimensional or multidimensional.
The individual elements of the array age are:
age[0], age[1], age[2], age[3], ......... age[98], age[99].

B V Raju Institute of Technology


Data Structures Page: 3
Linked List: Linked list is a linear data structure which is used to maintain a list in the memory. It can
be seen as the collection of nodes stored at non-contiguous memory locations. Each node of the list
contains a pointer to its adjacent node.
Stack: Stack is a linear list in which insertion and deletions are allowed only at one end, called top.
A stack is an abstract data type (ADT), can be implemented in most of the programming languages.
It is named as stack because it behaves like a real-world stack, for example: - piles of plates or deck
of cards etc.
Queue: Queue is a linear list in which elements can be inserted only at one end called rear and deleted
only at the other end called front.
It is an abstract data structure, similar to stack. Queue is opened at both end therefore it follows First-
In-First-Out (FIFO) methodology for storing the data items.
Non Linear Data Structures: This data structure does not form a sequence i.e. each item or
element is connected with two or more other items in a non-linear arrangement. The data elements
are not arranged in sequential structure.
Types of Non Linear Data Structures are given below:
Trees: Trees are multilevel data structures with a hierarchical relationship among its elements known
as nodes. The bottommost nodes in the herierchy are called leaf node while the topmost node is called
root node. Each node contains pointers to point adjacent nodes.
Tree data structure is based on the parent-child relationship among the nodes. Each node in the tree
can have more than one children except the leaf nodes whereas each node can have atmost one parent
except the root node. Trees can be classfied into many categories which will be discussed later in this
tutorial.
Graphs: Graphs can be defined as the pictorial representation of the set of elements (represented by
vertices) connected by the links known as edges. A graph is different from tree in the sense that a
graph can have cycle while the tree can not have the one.

Operations on data structure


1) Traversing: Every data structure contains the set of data elements. Traversing the data structure
means visiting each element of the data structure in order to perform some specific operation like
searching or sorting.
Example: If we need to calculate the average of the marks obtained by a student in 6 different subject,
we need to traverse the complete array of marks and calculate the total sum, then we will devide that
sum by the number of subjects i.e. 6, in order to find the average.
2) Insertion: Insertion can be defined as the process of adding the elements to the data structure at
any location.
If the size of data structure is n then we can only insert n-1 data elements into it.
3) Deletion: The process of removing an element from the data structure is called Deletion. We can
delete an element from the data structure at any random location.
If we try to delete an element from an empty data structure, then underflow occurs.

B V Raju Institute of Technology


Data Structures Page: 4
4) Searching: The process of finding the location of an element within the data structure is called
Searching. There are two algorithms to perform searching, Linear Search and Binary Search. We will
discuss each one of them later in this tutorial.
5) Sorting: The process of arranging the data structure in a specific order is known as Sorting. There
are many algorithms that can be used to perform sorting, for example, insertion sort, selection sort,
bubble sort, etc.
6) Merging: When two lists List A and List B of size M and N respectively, of similar type of elements,
clubbed or joined to produce the third list, List C of size (M+N), then this process is called merging

Algorithm
An algorithm is a procedure having well defined steps for solving a particular problem. Algorithm is
finite set of logic or instructions, written in order for accomplish the certain predefined task. It is not
the complete program or code, it is just a solution (logic) of a problem, which can be represented
either as an informal description using a Flowchart or Pseudo code.
The major categories of algorithms are given below:
o Sort: Algorithm developed for sorting the items in certain order.
o Search: Algorithm developed for searching the items inside a data structure.
o Delete: Algorithm developed for deleting the existing element from the data structure.
o Insert: Algorithm developed for inserting an item inside a data structure.
o Update: Algorithm developed for updating the existing element inside a data structure.
Each algorithm must have:
o Specification: Description of the computational procedure.
o Pre-conditions: The condition(s) on input.
o Body of the Algorithm: A sequence of clear and unambiguous instructions.
o Post-conditions: The condition(s) on output.
Characteristics of an Algorithm
An algorithm must follow the mentioned below characteristics:
o Input: An algorithm must have 0 or well defined inputs.
o Output: An algorithm must have 1 or well defined outputs, and should match with the
desired output.
o Feasibility: An algorithm must be terminated after the finite number of steps.
o Independent: An algorithm must have step-by-step directions which is independent of any
programming code.
o Unambiguous: An algorithm must be unambiguous and clear. Each of their steps and
input/outputs must be clear and lead to only one meaning.

B V Raju Institute of Technology


Data Structures Page: 5

Algorithm Analysis
Efficiency of an algorithm can be analyzed at two different stages, before implementation and after
implementation. They are the following −
• A Priori Analysis − This is a theoretical analysis of an algorithm. Efficiency of an algorithm is
measured by assuming that all other factors, for example, processor speed, are constant and
have no effect on the implementation.
• A Posterior Analysis − This is an empirical analysis of an algorithm. The selected algorithm
is implemented using programming language. This is then executed on target computer
machine. In this analysis, actual statistics like running time and space required, are collected.
We shall learn about a priori algorithm analysis. Algorithm analysis deals with the execution or running
time of various operations involved. The running time of an operation can be defined as the number
of computer instructions executed per operation.

Algorithm Complexity
Suppose X is an algorithm and n is the size of input data, the time and space used by the algorithm
X are the two main factors, which decide the efficiency of X.
• Time Factor − Time is measured by counting the number of key operations such as
comparisons in the sorting algorithm.
• Space Factor − Space is measured by counting the maximum memory space required by the
algorithm.
The complexity of an algorithm f(n) gives the running time and/or the storage space required by the
algorithm in terms of n as the size of input data.

Space Complexity
Space complexity of an algorithm represents the amount of memory space required by the algorithm
in its life cycle. The space required by an algorithm is equal to the sum of the following two components

• A fixed part that is a space required to store certain data and variables, that are independent
of the size of the problem. For example, simple variables and constants used, program size,
etc.
• A variable part is a space required by variables, whose size depends on the size of the problem.
For example, dynamic memory allocation, recursion stack space, etc.
Space complexity S(P) of any algorithm P is S(P) = C + SP(I), where C is the fixed part and S(I) is
the variable part of the algorithm, which depends on instance characteristic I. Following is a simple
example that tries to explain the concept −

Algorithm: SUM(A, B)
Step 1 - START
Step 2 - C ← A + B + 10

B V Raju Institute of Technology


Data Structures Page: 6

Step 3 - Stop

Here we have three variables A, B, and C and one constant. Hence S(P) = 1 + 3. Now, space depends
on data types of given variables and constant types and it will be multiplied accordingly.

Time Complexity
Time complexity of an algorithm represents the amount of time required by the algorithm to run to
completion. Time requirements can be defined as a numerical function T(n), where T(n) can be
measured as the number of steps, provided each step consumes constant time.
For example, addition of two n-bit integers takes n steps. Consequently, the total computational time
is T(n) = c ∗ n, where c is the time taken for the addition of two bits. Here, we observe that T(n)
grows linearly as the input size increases.

Asymptotic Analysis
In mathematical analysis, asymptotic analysis of algorithm is a method of defining the mathematical
boundation of its run-time performance. Using the asymptotic analysis, we can easily conclude about
the average case, best case and worst case scenario of an algorithm.
It is used to mathematically calculate the running time of any operation inside an algorithm.

Example: Running time of one operation is x(n) and for another operation it is calculated as f(n2). It
refers to running time will increase linearly with increase in 'n' for first operation and running time will
increase exponentially for second operation. Similarly the running time of both operations will be same
if n is significantly small.

Usually the time required by an algorithm comes under three types:

Worst case: It defines the input for which the algorithm takes the huge time.

Average case: It takes average time for the program execution.

Best case: It defines the input for which the algorithm takes the lowest time.

Asymptotic Notations
The commonly used asymptotic notations used for calculating the running time complexity
of an algorithm is given below:
o Big oh Notation (Ο)
o Omega Notation (Ω)
o Theta Notation (θ)

B V Raju Institute of Technology


Data Structures Page: 7

Big oh Notation (O)


It is the formal way to express the upper boundary of an algorithm running time. It measures the
worst case of time complexity or the longest amount of time, algorithm takes to complete their
operation. It is represented as shown below:

For example: If f(n) and g(n) are the two functions defined for positive integers, then f(n) is
O(g(n)) as f(n) is big oh of g(n)or f(n) is on the order of g(n)) if there exists constants c and no
such that:
F(n)≤cg(n) for all n≥no
This implies that f(n) does not grow faster than g(n), or g(n) is an upper bound on the function f(n).

Omega Notation (Ω)


It is the formal way to represent the lower bound of an algorithm's running time. It measures the best
amount of time an algorithm can possibly take to complete or the best case time complexity.
If we required that an algorithm takes at least certain amount of time without using an upper bound,
we use big- Ω notation i.e. the Greek letter "omega". It is used to bound the growth of running time
for large input size.
If running time is Ω (f(n)), then for the larger value of n, the running time is at least k?f(n) for
constant (k). It is represented as shown below:

B V Raju Institute of Technology


Data Structures Page: 8

Theta Notation (θ)


It is the formal way to express both the upper bound and lower bound of an algorithm running time.
Consider the running time of an algorithm is θ (n), if at once (n) gets large enough the running time
is at most k2-n and at least k1 ?n for some constants k1 and k2. It is represented as shown below:

Common Asymptotic Notations

constant − Ο(1)

logarithmic − Ο(log n)

linear − Ο(n)

n log n − Ο(n log n)

quadratic − Ο(n2)

cubic − Ο(n3)

polynomial − nΟ(1)

exponential − 2Ο(n)

B V Raju Institute of Technology


Data Structures Page: 9

Recursion
What is Recursion?

The process in which a function calls itself directly or indirectly is called recursion and the corresponding
function is called as recursive function. Using recursive algorithm, certain problems can be solved quite
easily. Examples of such problems are Towers of Hanoi (TOH), Inorder/Preorder/Postorder Tree
Traversals, DFS of Graph, etc.

What is base condition in recursion?

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.
How a particular problem is solved using recursion?

The idea is to represent a problem in terms of one or more smaller problems, and add one or more
base conditions that stop the recursion. For example, we compute factorial n if we know factorial of
(n-1). The base case for factorial would be n = 0. We return 1 when n = 0.

Why Stack Overflow error occurs in recursion?

If the base case is not reached or not defined, then the stack overflow problem may arise. Let us take
an example to understand this.
int fact(int n)
{
// wrong base case (it may cause
// stack overflow).
if (n == 100)

B V Raju Institute of Technology


Data Structures Page: 10
return 1;

else
return n*fact(n-1);
}

If fact(10) is called, it will call fact(9), fact(8), fact(7) and so on but the number will never reach 100.
So, the base case is not reached. If the memory is exhausted by these functions on the stack, it will
cause a stack overflow error.
What is the difference between direct and indirect recursion?

A function fun is called direct recursive if it calls the same function fun. A function fun is called indirect
recursive if it calls another function say fun_new and fun_new calls fun directly or indirectly. Difference
between direct and indirect recursion has been illustrated in Table 1.

// An example of direct recursion


void directRecFun()
{
// Some code....

directRecFun();

// Some code...
}

// An example of indirect recursion


void indirectRecFun1()
{
// Some code...

indirectRecFun2();

// Some code...
}
void indirectRecFun2()
{
// Some code...

indirectRecFun1();

// Some code...
}

B V Raju Institute of Technology


Data Structures Page: 11
How memory is allocated to different function calls in recursion?
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.

Example
void printFun(int test)
{
if (test < 1)
return;
else
{
printf(“%d ”, test);
printFun(test-1);
printf(“%d ”, test);
return;
}
}

int main()
{
int test = 3;
printFun(test);
}

Output:

321123

When printFun(3) is called from main(), memory is allocated to printFun(3) and a local variable test
is initialized to 3 and statement 1 to 4 are pushed on the stack as shown in below diagram. It first prints
‘3’. In statement 2, printFun(2) is called and memory is allocated to printFun(2) and a local variable
test is initialized to 2 and statement 1 to 4 are pushed in the stack. Similarly,
printFun(2) calls printFun(1)and printFun(1) calls printFun(0). printFun(0) goes to if statement
and it return to printFun(1). Remaining statements of printFun(1) are executed and it returns to
printFun(2) and so on. In the output, value from 3 to 1 are printed and then 1 to 3 are printed. The
memory stack has been shown in below diagram.

B V Raju Institute of Technology


Data Structures Page: 12

What are the disadvantages of recursive programming over iterative programming?


Note that both recursive and iterative programs have the same problem-solving powers, i.e., every
recursive program can be written iteratively and vice versa is also true. The recursive program has
greater space requirements than iterative program as all functions will remain in the stack until the
base case is reached. It also has greater time requirements because of function calls and returns
overhead.
What are the advantages of recursive programming over iterative programming?
Recursion provides a clean and simple way to write code. Some problems are inherently recursive like
tree traversals, Tower of Hanoi, etc. For such problems, it is preferred to write recursive code. We can
write such codes also iteratively with the help of a stack data structure. For example refer Inorder Tree
Traversal without Recursion, Iterative Tower of Hanoi.

Types of Recursion
A function is recursive if it makes a call to itself directly or indirectly. If a function f() calls itself within
from its own body, it is called recursive. Secondly, if a function f() calls another function g() that
ultimately calls back to f(), then it can also be considered a recursive function. Following variants of
recursion tell us making recursive calls in different ways depending upon the problem.

Linear Recursion
In linear recursion a function calls exactly once to itself each time the function is invoked, and grows
linearly in proportion to the size of the problem. Finding maximum among an array of integers could
be a good example of linear recursion. The same is demonstrated below:

B V Raju Institute of Technology


Data Structures Page: 13
Let's assume that we are given an array arr of n decimal integers and we have to find the maximum
element from the given array. We can solve this problem using linear recursion by observing that the
maximum among all integers in arr is arr[0], if n==1, or the maximum of first n-1 in arr and the last
element in arr.

/*
File: rec_max_array.c
Program: Find maximum among array elements
recursively.
*/
#include <stdio.h>
#define SIZE 10

int recursiveMax (int *, int );


int max (int, int);
int main ()
{
int arr[SIZE] = {1, 3, 5, 4, 7, 19, 6, 11, 10, 2};
int max = recursiveMax(arr, SIZE);
printf("Maximum element among array items is: %d\n", max);

}
int recursiveMax (int *arr, int n)
{
if (n == 1)
return arr[0];

return max (recursiveMax (arr, n-1), arr[n-1]);


}
/* helper function to compute max of two decimal integers */
int max (int n, int m)
{
if (n > m) return n;
return m;
}

OUTPUT
======
Maximum element among array items is: 19

B V Raju Institute of Technology


Data Structures Page: 14
Tail Recursion
Tail Recursion is another form of linear recursion, where the function makes a recursive call as its very
last operation. Note that, there is a difference between last operation and last statement. If you see at
recursiveMax (defined above), then you will find that recursive call to function recursiveMax is last
statement but not last operation, code arr[n-1] yet to be processed to complete the execution of last
statement.
herefore, a function will be said tail recursive if the recursive call is the last thing the function performs.
When a function uses tail recursion, it can be converted to the non-recursive one, by iterating through
the recursive calls rather than calling them explicitly. Reversing array elements could be a good
example of tail recursion. The recursive solution of reversing array elements problem is illustrated as
below, followed by it's iterative version.
Suppose, we are given an array of decimal integers, and we have to reverse the order of elements in
which they are stored. We can solve this problem by using linear recursion. Reversal of an array can
be achieved by swapping the first and last elements and then recursively reversing the remaining
elements in the array.

/*
File: rec_array_reverse.c
Program: Reverse array elements recursively.
*/

#include <stdio.h>
#define SIZE 10

void recursiveRev (int *, int, int);

int main ()
{
int arr[SIZE] = {1, 2, 3, 4, 5, 6, 7, 8, 9 , 10};
int i;
recursiveRev (arr, 0, SIZE-1);

printf ("Printing array elements after reversing them...\n");


for (i = 0; i < SIZE; i++)
printf ("%d\n", arr[i]);
}

void recursiveRev (int * arr, int i, int j)


{
if(i < j)
{
int tmp;

B V Raju Institute of Technology


Data Structures Page: 15

tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
recursiveRev (arr, i+1, j-1);
}
}

OUTPUT
======
Printing array elements after reversing them...
10
9
8
7
6
5
4
3
2
1

Binary Recursion
As name suggests, in binary recursion a function makes two recursive calls to itself when invoked, it
uses binary recursion. Fibonacci series is a very nice example to demonstrate binary recursion. See
the example below:
fib (1) = fib (2) = 1
fib (n) = fib (n-1) + fib (n-2), if n > 2

By definition, the first two numbers in the Fibonacci sequence are 0 and 1, and each subsequent
number is the sum of the previous two [Definition Source wikipedia].

/*
File: bin_rec_fib.c
Program: Computes n-th fibonacci number using
binary recursion.*/
#include <stdio.h>
int recursiveFib (int);
int main ()
{
int n = 6;
printf ("%dth fibonacci number is %d\n", n, recursiveFib(n));
}
int recursiveFib (int n)

B V Raju Institute of Technology


Data Structures Page: 16

// base case
if (n <= 1)
return n; // binary recursive call
return recursiveFib(n-1) + recursiveFib (n - 2);

OUTPUT
======
6th fibonacci number is 8

Above piece of code exercises binary recursion in order to compute 6th fibonacci number. In this piece
of code recursiveFib returns n in case n is less than or equal to 1. This is the base condition in
recursiveFib from where recursive call returns to previous call. Following figure shows how
recursiveFib is executed.

Multiple Recursion
Multiple recursion can be treated a generalized form of binary recursion. When a function makes
multiple recursive calls possibly more than two, it is called multiple recursion.

Towers of Hanoi:
It is a mathematical puzzle, is also called tower of Brahma or Lucas tower. It consists of three pegs or rods and
number of disks of different sizes which can slide onto any peg.

At the beginning of the game, all the plates are arranged in ascending order on peg A that is smallest on top and
biggest on bottom.

The objective of this game is to move all disks from peg A to peg C using auxiliary peg B using some rules.
1. Only one disk must be moved at a time
2. Only top disk must be moved and placed only on top
3. A disk can’t be placed on small disk
4. Game must be completed within 2n-1 steps
Now let us play the game with two plates

B V Raju Institute of Technology


Data Structures Page: 17

Let us play the game with three plates now

It is difficult to play the game as the number of plates grows. The recursive solution to towers of
Hanoi is

tower(beg,end,aux,n-1)
beg->end
tower(aux,beg,end,n-1)

Equivalent implementation in C language is


void tower(char beg,char aux,char end,int n)

if(n==0)

return;

tower(beg,end,aux,n-1);

printf("\n%c -> %c",beg,end);

tower(aux,beg,end,n-1);

B V Raju Institute of Technology


Data Structures Page: 18
}

Let us test it with 3 disks A, B and C

#include<stdio.h>
void tower(char,char,char,int);
int main()
{
int n;
printf("nEnter number of plates:");
scanf("%d",&n);
tower('A','B','C',n);
return 0;
}
void tower(char beg,char aux,char end,int n)
{
if(n==0)
return;
tower(beg,end,aux,n-1);
printf("\n%c -> %c",beg,end);
tower(aux,beg,end,n-1);
}

B V Raju Institute of Technology


Data Structures Page: 19
Execution:
Enter number of plates:2
A -> B
A -> C
B -> C

Execution:
Enter number of plates:3
A -> C
A -> B
C -> B
A -> C
B -> A
B -> C
A -> C

Few words about origin:


Origin of this puzzle is an Indian temple called “Kashi Vishwanath” in India. The temple has a big room with three
pegs and 64 golden plates. According to the rules of Brahma (God of creation), if any body completes the game
then the last move of game would be the end of world.

Later a French mathematician “Edouard Lucas” in 1883 published the game in west. According to Lucas, it takes
264-1 seconds to complete the game if it takes 1 sec to move a plate, which is equal to 585 billion years.

B V Raju Institute of Technology


Data Structures Page: 20

Searching
Searching is the process of finding some particular element in the list. If the element is present in
the list, then the process is called successful and the process returns the location of that element,
otherwise the search is called unsuccessful.
There are two popular search methods that are widely used in order to search some item into the
list. However, choice of the algorithm depends upon the arrangement of the list.

o Linear Search
o Binary Search
o Fibonacci Search

Linear Search
Linear search is the simplest search algorithm and often called sequential search. In this type of
searching, we simply traverse the list completely and match each element of the list with the item
whose location is to be found. If the match found then location of the item is returned otherwise the
algorithm return NULL.
Linear search is mostly used to search an unordered list in which the items are not sorted. The
algorithm of linear search is given as follows.

Algorithm
o LINEAR_SEARCH(A, N, VAL)
o Step 1: [INITIALIZE] SET POS = -1
o Step 2: [INITIALIZE] SET I = 1
o Step 3: Repeat Step 4 while I<=N
o Step 4: IF A[I] = VAL
SET POS = I
PRINT POS
Go to Step 6
[END OF IF]
SET I = I + 1
[END OF LOOP]
o Step 5: IF POS = -1
PRINT " VALUE IS NOT PRESENTIN THE ARRAY "
[END OF IF]
o Step 6: EXIT

B V Raju Institute of Technology


Data Structures Page: 21

Complexity of algorithm

Complexity Best Case Average Case Worst Case

Time O(1) O(n) O(n)

Space O(1)

Program
#include<stdio.h>
int linearSearch(int arr[], int n, int key)
{
if(n < 0)
return -1;
else if(arr[n] == key)
return n;
else
return linearSearch(arr, n-1, key);
}

int main()
{
int arr[50], n, i, key, pos;

printf("\n Enter the no.of elements: ");


scanf("%d", &n);

printf("\n Enter %d elements: ", n);


for(i=0; i<n; i++)
scanf("%d", &arr[i]);

printf("\n Enter the key to search: ");


scanf("%d", &key);

pos = linearSearch(arr, n-1, key);

if(pos == -1)
printf("\n Key %d not found.", key);
else
printf("\n Key %d found at position: %d", key, pos+1);

return 0;
}

B V Raju Institute of Technology


Data Structures Page: 22

Binary Search
Binary search is the search technique which works efficiently on the sorted lists. Hence, in order to
search an element into some list by using binary search technique, we must ensure that the list is
sorted.
Binary search follows divide and conquer approach in which, the list is divided into two halves and the
item is compared with the middle element of the list. If the match is found then, the location of middle
element is returned otherwise, we search into either of the halves depending upon the result produced
through the match.
Binary search algorithm is given below.

BINARY_SEARCH(A, lower_bound, upper_bound, VAL)


o Step 1: [INITIALIZE] SET BEG = lower_bound
END = upper_bound, POS = - 1
o Step 2: Repeat Steps 3 and 4 while BEG <=END
o Step 3: SET MID = (BEG + END)/2
o Step 4: IF A[MID] = VAL
SET POS = MID
PRINT POS
Go to Step 6
ELSE IF A[MID] > VAL
SET END = MID - 1
ELSE
SET BEG = MID + 1
[END OF IF]
[END OF LOOP]
o Step 5: IF POS = -1
PRINT "VALUE IS NOT PRESENT IN THE ARRAY"
[END OF IF]
o Step 6: EXIT

Complexity
SN Performance Complexity

1 Worst case O(log n)

2 Best case O(1)

3 Average Case O(log n)

4 Worst case space complexity O(1)

B V Raju Institute of Technology


Data Structures Page: 23
Example

Let us consider an array arr = {1, 5, 7, 8, 13, 19, 20, 23, 29}. Find the location of the item
23 in the array.

In 1st step :

1. BEG = 0
2. END = 8ron
3. MID = 4
4. a[mid] = a[4] = 13 < 23, therefore

in Second step:

1. Beg = mid +1 = 5
2. End = 8
3. mid = 13/2 = 6
4. a[mid] = a[6] = 20 < 23, therefore;

in third step:

1. beg = mid + 1 = 7
2. End = 8
3. mid = 15/2 = 7
4. a[mid] = a[7]
5. a[7] = 23 = item;
6. therefore, set location = mid;
7. The location of the item will be 7.

B V Raju Institute of Technology


Data Structures Page: 24
Program

#include<stdio.h>
int binarySearch(int arr[], int first, int last, int key)
{
if(first <= last)
{
int mid;
mid = (first + last) / 2;
if(arr[mid] == key)
return mid;
else if(key < arr[mid])
return binarySearch(arr, first, mid-1, key);
else
return binarySearch(arr, mid+1, last, key);
}

return -1;
}
int main()
{
int arr[50], n, i, key, pos;

printf("\n Enter the no.of elements: ");


scanf("%d", &n);

printf("\n Enter %d elements in ascending order: ", n);


for(i=0; i<n; i++)
scanf("%d", &arr[i]);

printf("\n Enter the key to search: ");


scanf("%d", &key);

pos = binarySearch(arr, 0, n-1, key);


if(pos == -1)
printf("\n Key %d not found.", key);
else
printf("\n Key %d found at position: %d", key, pos+1);

return 0;
}

B V Raju Institute of Technology


Data Structures Page: 25

Fibonacci Search
Fibonacci Search is a comparison-based technique that uses Fibonacci numbers to search an element
in a sorted array.
Similarities with Binary Search:
1. Works for sorted arrays
2. A Divide and Conquer Algorithm.
3. Has Log n time complexity.
Differences with Binary Search:
1. Fibonacci Search divides given array in unequal parts
2. Binary Search uses division operator to divide range. Fibonacci Search doesn’t use /, but uses +
and -. The division operator may be costly on some CPUs.
3. Fibonacci Search examines relatively closer elements in subsequent steps. So when input array
is big that cannot fit in CPU cache or even in RAM, Fibonacci Search can be useful.
Background:
Fibonacci Numbers are recursively defined as F(n) = F(n-1) + F(n-2), F(0) = 0, F(1) = 1. First few
Fibinacci Numbers are 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, …
Observations:
Below observation is used for range elimination, and hence for the O(log(n)) complexity .

Algorithm:
Let the searched element be x.
The idea is to first find the smallest Fibonacci number that is greater than or equal to the length of
given array. Let the found Fibonacci number be fib (m’th Fibonacci number). We use (m-2)’th Fibonacci
number as the index (If it is a valid index). Let (m-2)’th Fibonacci Number be i, we compare arr[i] with
x, if x is same, we return i. Else if x is greater, we recur for subarray after i, else we recur for subarray
before i.

Below is the complete algorithm

Let arr[0..n-1] be the input array and element to be searched be x.


1. Find the smallest Fibonacci Number greater than or equal to n. Let this number be fibM [m’th
Fibonacci Number]. Let the two Fibonacci numbers preceding it be fibMm1 [(m-1)’th Fibonacci
Number] and fibMm2 [(m-2)’th Fibonacci Number].
2. While the array has elements to be inspected:
1. Compare x with the last element of the range covered by fibMm2
2. If x matches, return index

B V Raju Institute of Technology


Data Structures Page: 26
3. Else If x is less than the element, move the three Fibonacci variables two Fibonacci down,
indicating elimination of approximately rear two-third of the remaining array.
4. Else x is greater than the element, move the three Fibonacci variables one Fibonacci down.
Reset offset to index. Together these indicate elimination of approximately front one-third
of the remaining array.
3. Since there might be a single element remaining for comparison, check if fibMm1 is 1. If Yes,
compare x with that remaining element. If match, return index.

Program
#include<stdio.h>

int fibonacciSearch(int arr[], int n, int key)


{
int offset = -1;
/* For finding the smallest Fibonacci number (fbM) which is >= n */
int fbM2 = 0;
int fbM1 = 1;
int fbM = fbM2 + fbM1;
while (fbM < n)
{
fbM2 = fbM1;
fbM1 = fbM;
fbM = fbM2 + fbM1;
}

/* For finding key element index in the given list */


while (fbM > 1)
{
int i = offset + fbM2;
if(i >= n)
{
/* If i value is out of the range */
return -1;
}
else
{
if(arr[i] == key)
{
return i;
}
else if (key > arr[i])
{
/* If the key is > to the array element then go one step down */

B V Raju Institute of Technology


Data Structures Page: 27
fbM = fbM1;
fbM1 = fbM2;
fbM2 = fbM - fbM1;
/* Change the offset value to eliminate unnecessary elements */
offset = i;
}
else
{
/* If the key is > to the array element then go two steps down */
fbM = fbM2;
fbM1 = fbM1 - fbM2;
fbM2 = fbM - fbM1;
}
}
}

B V Raju Institute of Technology


Data Structures Page: 28

if(arr[0] == key)
{
/* When array is having only
one element */ return 0;
}
return -1;
}
int main(void)
{
int arr[50], n, i, key, pos;
printf("\n Enter the number of
elements: "); scanf("%d", &n);
printf("\n Enter %d
elements: ", n); for(i=0;
i<n; i++)
{
scanf("%d", &arr[i]);
}
printf("\n Enter element to be
searched: "); scanf("%d",
&key);
pos =
fibonacciSearch(a
rr, n, key); if(pos
== -1)
printf("\n ** Key not found ** \n");
else
printf("\n Key Found at index: %d", pos);
return 0;
}

B V Raju Institute of Technology

You might also like