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

Unit-5 - Data Structures Using C

Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
8 views

Unit-5 - Data Structures Using C

Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 16

Linear Search Algorithm

(Sequential Search)
What is Search?
Search is a process of finding a value in a list of values. In other words, searching is the process of
locating given value position in a list of values.

Linear Search Algorithm (Sequential Search Algorithm)


Linear search algorithm finds given element in a list of elements with O(n) time complexity where n is
total number of elements in the list.

Linear search is implemented using following steps...


 Step 1: Read the search element from the user
 Step 2: Compare, the search element with the first element in the list.
 Step 3: If both are matching, then display "Given element found!!!" and terminate the function
 Step 4: If both are not matching, then compare search element with the next element in the
list.
 Step 5: Repeat steps 3 and 4 until the search element is compared with the last element in
the list.
 Step 6: If the last element in the list is also doesn't match, then display "Element not found!!!"
and terminate the function.

Example
Consider the following list of element and search element...

Linear Search Program in C Programming Language


#include<stdio.h>
#include<conio.h>

void main(){
int list[20],size,i,sElement;

printf("Enter size of the list: ");


scanf("%d",&size);

printf("Enter any %d integer values: ",size);


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

printf("Enter the element to be Search: ");


scanf("%d",&sElement);

1
// Linear Search Logic
for(i = 0; i < size; i++)
{
if(sElement == list[i])
{
printf("Element is found at %d index", i);
break;
}
}
if(i == size)
printf("Given element is not found in the list!!!");
getch();
}

Binary Search Algorithm


Binary search algorithm finds given element in a list of elements with O (log n) time complexity
where n is total number of elements in the list. The binary search algorithm can be used with only
sorted list of element. That means, binary search can be used only with list of element which are
already arranged in an order.

Binary search is implemented using following steps...


 Step 1: Read the search element from the user
 Step 2: Find the middle element in the sorted list
 Step 3: Compare, the search element with the middle element in the sorted list.
 Step 4: If both are matching, then display "Given element found!!!" and terminate the function
 Step 5: If both are not matching, then check whether the search element is smaller or larger
than middle element.
 Step 6: If the search element is smaller than middle element, then repeat steps 2, 3, 4 and 5
for the left sublist of the middle element.
 Step 7: If the search element is larger than middle element, then repeat steps 2, 3, 4 and 5
for the right sublist of the middle element.
 Step 8: Repeat the same process until we find the search element in the list or until sublist
contains only one element.
 Step 9: If that element also doesn't match with the search element, then display "Element not
found in the list!!!" and terminate the function.

Example
Consider the following list of element and search element 4...

2
Binary Search Program in C Programming Language
#include<stdio.h>
#include<conio.h>

void main()
{
int first, last, middle, size, i, sElement, list[100];
clrscr();

printf("Enter the size of the list: ");


scanf("%d",&size);

printf("Enter %d integer values in Assending order\n", size);

for (i = 0; i < size; i++)


scanf("%d",&list[i]);

printf("Enter value to be search: ");


scanf("%d", &sElement);

first = 0;
last = size - 1;
middle = (first+last)/2;

while (first <= last) {


if (list[middle] < sElement)
first = middle + 1;
else if (list[middle] == sElement) {
printf("Element found at index %d.\n",middle);
break;
}
else
last = middle - 1;

middle = (first + last)/2;


}
if (first > last)
printf("Element Not found in the list.");
getch();
}

3
Sorting Techniques
Sorting is the process of arranging a list of elements in a particular order (Ascending or Descending).

Selection Sort
Selection Sort algorithm is used to arrange a list of elements in a particular order (Ascending or
Descending). In selection sort, the first element in the list is selected and it is compared repeatedly
with remaining all the elements in the list. If any element is smaller than the selected element (for
Ascending order), then both are swapped. Then we select the element at second position in the list
and it is compared with remaining all elements in the list. If any element is smaller than the selected
element, then both are swapped. This procedure is repeated till the entire list is sorted.

Step by Step Process


The selection sort algorithm is performed using following steps...
 Step 1: Select the first element of the list (i.e., Element at first position in the list).
 Step 2: Compare the selected element with all other elements in the list.
 Step 3: For every comparison, if any element is smaller than selected element (for Ascending
order), then these two are swapped.
 Step 4: Repeat the same procedure with next position in the list till the entire list is sorted.

#include <stdio.h>
#include <conio.h>

int main()
{
int array[100], n, c, d, swap;

clrscr();

printf("Enter number of elements\n");


scanf("%d", &n);

printf("Enter %d integers\n", n);

for (c = 0; c < n; c++)


scanf("%d", &array[c]);

for (c = 0 ; c < ( n - 1 ); c++)


{
for (d = 0 ; d < n - c - 1; d++)
{
if (array[d] > array[d+1]) /* For decreasing order use < */
{
swap = array[d];
array[d] = array[d+1];
array[d+1] = swap;
}
}
}

printf("Sorted list in ascending order:\n");

for ( c = 0 ; c < n ; c++ )


printf(" %d ", array[c]);
getch();
return 0;
}
4
Insertion Sort
Insertion sort algorithm arranges a list of elements in a particular order. In insertion sort algorithm,
every iteration moves an element from unsorted portion to sorted portion until all the elements are
sorted in the list.
Step by Step Process
The insertion sort algorithm is performed using following steps...
 Step 1: Asume that first element in the list is in sorted portion of the list and remaining all
elements are in unsorted portion.
 Step 2: Consider first element from the unsorted list and insert that element into the sorted list
in order specified.
 Step 3: Repeat the above process until all the elements from the unsorted list are moved into
the sorted list.
#include<stdio.h>
#include<conio.h>
/* Logic : Suppose if the array is sorted till index i then we can sort the arry
till i+1 by inserting i+1 th element in the correct position from 0 to i+1. The
position at which (i+1)th element has to be inserted has to be found by iterating
from 0 to i. As any array is sorted till 0th position (Single element is always
sorted) and we know how to expand, we can sort the whole array */

void InsertionSort(int *array , int number_of_elements)


{
int iter,jter;
for(iter=1;iter<number_of_elements;iter++)
{
int current_element = array[iter];
jter = iter-1;
while(jter>=0 && array[jter] > current_element)
{
array[jter+1] = array[jter];
jter--;
}
array[jter+1] = current_element;
}
}
int main()
{
int number_of_elements;
int array[10];
int iter;

clrscr();
printf("\nProgram For Insertion Sort ");
printf("\nEnter number of elements : ");
scanf("%d",&number_of_elements);

for(iter = 0;iter < number_of_elements;iter++)


{
scanf("%d",&array[iter]);
}

/* Calling this functions sorts the array */


printf("\nInsertion Sort is : ");
InsertionSort(array,number_of_elements);
for(iter = 0;iter < number_of_elements;iter++)
{
printf("%d ",array[iter]);

5
}
printf("\n");
getch();
return 0;
}

Bubble Sorting
Bubble Sort is an algorithm which is used to sort N elements that are given in a memory for eg: an
Array with N number of elements. Bubble Sort compares all the element one by one and sort them
based on their values.
It is called Bubble sort, because with each iteration the smaller element in the list bubbles up towards
the first place, just like a water bubble rises up to the water surface.
Sorting takes place by stepping through all the data items one-by-one in pairs and comparing
adjacent data items and swapping each pair that is out of order.

Sorting using Bubble Sort Algorithm and Program


#include <stdio.h>
#include <conio.h>

void main( )
{
int arr[5] = { 25, 17, 31, 13, 2 } ;
int i, j, temp ;

clrscr( ) ;

printf ( "Bubble sort.\n" ) ;


printf ( "\nArray before sorting:\n") ;

for ( i = 0 ; i <= 4 ; i++ )


printf ( "%d\t", arr[i] ) ;

for ( i = 0 ; i <= 3 ; i++ )


{
for ( j = 0 ; j <= 3 - i ; j++ )
{
if ( arr[j] > arr[j + 1] )
{
temp = arr[j] ;
arr[j] = arr[j + 1] ;
arr[j + 1] = temp ;
}
}
}
6
printf ( "\n\nArray after sorting:\n") ;

for ( i = 0 ; i <= 4 ; i++ )


printf ( "%d\t", arr[i] ) ;

getch( ) ;
}

Quick Sort Algorithm


Quick Sort, as the name suggests, sorts any list very quickly. Quick sort is not stable search, but it is
very fast and requires very less aditional space. It is based on the rule of Divide and Conquer(also
called partition-exchange sort). This algorithm divides the list into three main parts :
1. Elements less than the Pivot element
2. Pivot element
3. Elements greater than the pivot element
In the list of elements, mentioned in below example, we have taken 25 as pivot. So after the first pass,
the list will be changed like this.
6 8 17 14 25 63 37 52
Hnece after the first pass, pivot will be set at its position, with all the elements smaller to it on its left
and all the elements larger than it on the right. Now 6 8 17 14 and 63 37 52 are considered as two
separate lists, and same logic is applied on them, and we keep doing this until the complete list is
sorted.How Quick Sorting Works

Sorting using Quick Sort Algorithm


/* a[] is the array, p is starting index, that is 0,
and r is the last index of array. */

void quicksort(int a[], int p, int r)


{
if(p < r)
{
int q;
q = partition(a, p, r);
quicksort(a, p, q);
7
quicksort(a, q+1, r);
}
}

int partition(int a[], int p, int r)


{
int i, j, pivot, temp;
pivot = a[p];
i = p;
j = r;
while(1)
{
while(a[i] < pivot && a[i] != pivot)
i++;
while(a[j] > pivot && a[j] != pivot)
j--;
if(i < j)
{
temp = a[i];
a[i] = a[j];
a[j] = temp;
}
else
{
return j;
}
}
}

Merge Sort Algorithm


Merge Sort follows the rule of Divide and Conquer. But it doesn't divides the list into two halves. In
merge sort the unsorted list is divided into N sublists, each having one element, because a list of one
element is considered sorted. Then, it repeatedly merge these sublists, to produce new sorted
sublists, and at lasts one sorted list is produced.
Merge Sort is quite fast, and has a time complexity of O(n log n). It is also a stable sort, which means
the "equal" elements are ordered in the same order in the sorted list.
How Merge Sort Works

8
Like we can see in the above example, merge sort first breaks the unsorted list into sorted sublists,
and then keep merging these sublists, to finlly get the complete sorted list.

Sorting using Merge Sort Algorithm


/* a[] is the array, p is starting index, that is 0,
and r is the last index of array. */

Lets take a[5] = {32, 45, 67, 2, 7} as the array to be sorted.

void mergesort(int a[], int p, int r)


{
int q;
if(p < r)
{
q = floor( (p+r) / 2);
mergesort(a, p, q);
mergesort(a, q+1, r);
merge(a, p, q, r);
}
}

void merge(int a[], int p, int q, int r)


{
int b[5]; //same size of a[]
int i, j, k;
k = 0;
i = p;
j = q+1;
while(i <= q && j <= r)
{
if(a[i] < a[j])
{
b[k++] = a[i++]; // same as b[k]=a[i]; k++; i++;
}
else
{
b[k++] = a[j++];
}
}

while(i <= q)
{
b[k++] = a[i++];
}

while(j <= r)
{
b[k++] = a[j++];
}

for(i=r; i >= p; i--)


{
a[i] = b[--k]; // copying back the sorted list to a[]
}

9
Radix sort
Radix sort was developed for sorting large integers, but it treats an integer as a string of digits, so it is
really a string sorting algorithm.
Radix sort is a non-comparative sorting algorithm that sorts data with keys by grouping keys by the
individual digits which share the same significant position and value.
Radix Sort arranges the elements in order by comparing the digits of the numbers.

LSD radix sort Least-significant-digit-first radix sort.


LSD radix sorts process the integer representations starting from the least significant digit and move
the processing towards the most significant digit.

MSD radix sort Most-significant-digit-first radix sort.


MSD radix sort starts processing the keys from the most significant digit, leftmost digit, to the least
significant digit, rightmost digit. This sequence is opposite that of least significant digit (LSD) radix
sorts.

c Fuction for radix sort


radix_sort(int arr[], int n)
{
int bucket[10][5],buck[10],b[10];
int i,j,k,l,num,div,large,passes;
div=1;
num=0;
large=arr[0];
for(i=0 ; i< n ; i++)
{
if(arr[i] > large)
{
large = arr[i];
}

while(large > 0)
{
num++;
large = large/10;
}
for(passes=0 ; passes < num ; passes++)
{
for(k=0 ; k< 10 ; k++)
{
buck[k] = 0;
}
for(i=0 ; i< n ;i++)
{
l = ((arr[i]/div)%10);
bucket[l][buck[l]++] = arr[i];
}

i=0;
for(k=0 ; k < 10 ; k++)
{
for(j=0 ; j < buck[k] ; j++)
{
arr[i++] = bucket[k][j];
}
}
div*=10;
}
}
}

10
Shell sort
Shell sort is a highly efficient sorting algorithm and is based on insertion sort algorithm. This
algorithm avoids large shifts as in case of insertion sort, if the smaller value is to the far right and has
to be moved to the far left.
This algorithm uses insertion sort on a widely spread elements, first to sort them and then sorts the
less widely spaced elements. This spacing is termed as interval. This interval is calculated based on
Knuth's formula as −
Knuth's Formula
h = h * 3 + 1
where −
h is interval with initial value 1
This algorithm is quite efficient for medium-sized data sets as its average and worst case complexity
are of Ο(n), where n is the number of items.
How Shell Sort Works?
Let us consider the following example to have an idea of how shell sort works. We take the same
array we have used in our previous examples. For our example and ease of understanding, we take
the interval of 4. Make a virtual sub-list of all values located at the interval of 4 positions. Here these
values are {35, 14}, {33, 19}, {42, 27} and {10, 14}

We compare values in each sub-list and swap them (if necessary) in the original array. After this
step, the new array should look like this −

Then, we take interval of 2 and this gap generates two sub-lists - {14, 27, 35, 42}, {19, 10, 33, 44}

11
We compare and swap the values, if required, in the original array. After this step, the array should
look like this −

Finally, we sort the rest of the array using interval of value 1. Shell sort uses insertion sort to sort the
array.

Algorithm
Following is the algorithm for shell sort.

Step 1 − Initialize the value of h


Step 2 − Divide the list into smaller sub-list of equal interval h
Step 3 − Sort these sub-lists using insertion sort
Step 3 − Repeat until complete list is sorted

void shellSort(int numbers[], int array_size)


{
int i, j, increment, temp;

increment = 3;
while (increment > 0)
{
for (i=0; i < array_size; i++)
{
j = i;
temp = numbers[i];
while ((j >= increment) && (numbers[j-increment] > temp))
{
numbers[j] = numbers[j - increment];
j = j - increment;
}
numbers[j] = temp;

12
}
if (increment/2 != 0)
increment = increment/2;
else if (increment == 1)
increment = 0;
else
increment = 1;
}
}

Hash Table
Direct-address table
If the keys are drawn from the reasoning small universe U = {0, 1, . . . , m-1} of keys, a solution is to
use a Table T[0, . m-1], indexed by keys. To represent the dynamic set, we use an array, or direct-
address table, denoted by T[0 . . m-1], in which each slot corresponds to a key in the universe.
Following figure illustrates the approach.

Each key in the universe U i.e., Collection, corresponds to an index in the table T[0 . . m-1]. Using this
approach, all three basic operations (dictionary operations) take θ(1) in the worst case.
Hash Tables
When the size of the universe is much larger the same approach (direct address table) could still work
in principle, but the size of the table would make it impractical. A solution is to map the keys onto a
small range, using a function called a hash function. The resulting data structure is called hash table.
With direct addressing, an element with key k is stored in slot k. With hashing =, this same element is
stored in slot h(k); that is we use a hash function h to compute the slot from the key. Hash function
maps the universe U of keys into the slot of a hash table T[0 . . .m-1].
h: U → {0, 1, . . ., m-1}
More formally, suppose we want to store a set of size n in a table of size m. The ratio α = n/m is called
a load factor, that is, the average number of elements stored in a Table. Assume we have a hash
function h that maps each key k U to an integer name h(k) [0 . . m-1]. The basic idea is to store
key k in location T[h(k)].

13
Typical, hash functions generate "random looking" valves. For example, the following function usually
works well
h(k) = k mod m where m is a prime number.
Is there any point of the hash function? Yes, the point of the hash function is to reduce the range of
array indices that need to be handled.

Collision
As keys are inserted in the table, it is possible that two keys may hash to the same table slot. If the
hash function distributes the elements uniformly over the table, the number of conclusions cannot be
too large on the average, but the birthday paradox makes it very likely that there will be at least one
collision, even for a lightly loaded table

A hash function h map the keys k and j to the same slot, so they collide.
There are two basic methods for handling collisions in a hash table: Chaining and Open addressing.

Collision Resolution by Chaining


When there is a collision (keys hash to the same slot), the incoming keys is stored in an overflow area
and the corresponding record is appeared at the end of the linked list.

Each slot T[j] contains a linked list of all the keys whose hash value is j. For example, h(k1) =
h(kn) and h(k5) = h(k2) = h(k7).
14
 The worst case running time for insertion is O(1).
 Deletion of an element x can be accomplished in O(1) time if the lists are doubly linked.
 In the worst case behavior of chain-hashing, all n keys hash to the sameslot, creating a list of
length n. The worst-case time for search is thus θ(n) plus the time to compute the hash function.

keys: 5, 28, 19, 15, 20, 33, 12, 17, 10


slots: 9
hash function = h(k) = k mod 9
h(5) = 5 mod 9 = 4
h(28) = 28 mod 9 = 1
h(19) = 19 mod 9 = 1
h(15) = 15 mod 9 = 6
h(20) = 20 mod 9 = 2
h(33) = 33 mod 9 = 6
h(12) = 12mod 9 = 3
h(17) = 17 mod 9 = 8
h(10) = 10 mod 9 = 1

Figure
A good hash function satisfies the assumption of simple uniform hashing, each element is equally
likely to hash into any of the m slots, independently of where any other element has hash to. But
usually it is not possible to check this condition because one rarely knows the probability distribution
according to which the keys are drawn.
In practice, we use heuristic techniques to create a hash function that perform well. One good
approach is to derive the hash value in a way that is expected to be independent of any patterns that
might exist in the data (division method).
Most hash function assume that the universe of keys is the set of natural numbers. Thus, its keys are
not natural to interpret than as natural numbers.

Method for Creating Hash Function


1. The division method.
2. The multiplication method.
3. Universal hashing.

1. The Division Method


Map a key k into one of m slots by taking the remainder of k divided by m. That is, the hash function is
h(k) = k mod m.
Example: If table size m = 12, key k = 100; than
h(100) = 100 mod 12
=4
Poor choices of m
m should not be a power of 2, since if m = 2p, then h(k) is just the p lowest-order bits of k.
So, 2p may be a poor choice, because permuting the characters of k does not change value.
Good m choice of m
A prime not too close to an exact of 2.

15
2. The Multiplication Method
Two step process
Step 1: Multiply the key k by a constant 0< A < 1 and extract the fraction part of kA.
Step 2: Multiply kA by m and take the floor of the result.
The hash function using multiplication method is:
h(k) = ëm(kA mod 1)û
where "kA mod 1" means the fractional part of kA, that is, kA - ëkAû.

Advantage of this method is that the value of m is not critical and can be implemented on most
computers.
A reasonable value of constant A is
» (sqrt5 - 1) /2
suggested by Knuth's Art of Programming.

3. Universal Hashing
Open Addressing
This is another way to deal with collisions.
In this technique all elements are stored in the hash table itself. That is, each table entry contains
either an element or NIL. When searching for element (or empty slot), we systematically examine
slots until we found an element (or empty slot). There are no lists and no elements stored outside the
table. That implies that table can completely "fill up"; the load factor α can never exceed 1.Advantage
of this technique is that it avoids pointers (pointers need space too). Instead of chasing pointers, we
compute the sequence of slots to be examined. To perform insertion, we successively examine or
probe, the hash table until we find an empty slot. The sequence of slots probed "depends upon the
key being inserted." To determine which slots to probe, the hash function includes the probe number
as a second input. Thus, the hash function becomes

h: × {0, 1, . . . m -1 }--> {0, 1, . . . , m-1}


and the probe sequence
< h(k, 0), h(k, 1), . . . , h(k, m-1) >
in which every slot is eventually considered.

16

You might also like