You are on page 1of 21

Lab Manual

DESIGN & ANALYSIS OF ALGORITHMS LAB


Recommended Systems/Software Requirements

 Notepad
 JDK1.7

List of Programs
1. Program for Recursive Binary & Linear Search.
a. Iterative Linear Search
b. Recursive Linear Search
c. Divide and Conquer Binary Search
d. Recursive Binary Search
2. Program for Bubble Sort
3. Program for Insertion Sort.
4. Program for Selection Sort.
5. Program for Heap Sort.
6. Program for Merge Sort.
7. Program for Quick Sort.
8. Study of NP-Complete theory.
9. Study of Cook’s theorem.
10. Study of sorting network.

1
Programs with Solution

1. Program for Recursive Binary & Linear Search

a. Iterative Linear Search (with User Input)

import java.util.Scanner;

public class MyLinearSearch {


     public static int linerSearch(int[] arr, int key){
        int size = arr.length;
        for(int i=0;i<size;i++){
            if(arr[i] == key){
                return i;
            }
        }
        return -1;
    }
     
  public static void main(String a[]){
       Scanner input = new Scanner(System.in);

        System.out.print("Enter the size of the array: ");


        int size = input.nextInt();

        System.out.print("Enter an array of numbers: ");

        int[] arr = new int[size];


        for(int i=0; i<arr.length; i++)
        {
            arr[i]=input.nextInt();
        }

        System.out.print("Enter the number you want to search: ");


        int searchKey = input.nextInt();

int pos = linerSearch(arr, searchKey)


if (pos == -1)

          System.out.println("Key "+searchKey+" not found” );


else
System.out.println("Key "+searchKey+" found at index: "+(pos -1));

    }
}

2
b. Recursive Linear search
import java.util.Scanner;
class LinearSearch
{
    public static void main(String[] args)
    {
        Scanner input = new Scanner(System.in);
        System.out.print("Enter the size of the array: ");
        int size = input.nextInt();
        System.out.print("Enter an array of numbers: ");

        int[] arr = new int[size];


        for(int i=0; i<arr.length; i++)
        {
            arr[i]=input.nextInt();
        }

        System.out.print("Enter the number you want to search: ");


        int search = input.nextInt();

 Linear_Search access = new Linear_Search();


        System.out.print("The position of the search item is at array index ");
        access.linSearch2(arr, 0, arr.length, search);
    }
}
class Linear_Search
{
   public void linSearch2(int[] arr, int fIndex, int lIndex, int searchNum)
   {
   if(fIndex == lIndex)
      System.out.print("-1");
      else
   {
   if(arr[fIndex] == searchNum)
   {
   System.out.print(fIndex);
   }
   else
   {
   linSearch2(arr, fIndex+1, lIndex, searchNum);
   }
   }

3
   }
}

c. Binary Search by Divide and Conquer method


Requisite condition– sorted Array.

import java.util.Scanner;

public class MyBinarySearch {


 
    public int binarySearch(int[] inputArr, int key) {
         
        int start = 0;
        int end = inputArr.length - 1;
        while (start <= end) {
            int mid = (start + end) / 2;
            if (key == inputArr[mid]) {
                return mid;
            }
            if (key < inputArr[mid]) {
                end = mid - 1;
            } else {
                start = mid + 1;
            }
        }
        return -1;
    }
  
    public static void main(String[] args) {
         
        MyBinarySearch mbs = new MyBinarySearch();

Scanner input = new Scanner(System.in);


System.out.print("Enter the size of the array: ");
int size = input.nextInt();
System.out.print("Enter an array of numbers: ");

int[] arr = new int[size];


for(int i=0; i<arr.length; i++)
        {
            arr[i]=input.nextInt();
        }

System.out.print("Enter the number you want to search: ");


      int search = input.nextInt();

      System.out.println("Key "+ search+ " position: "+mbs.binarySearch(arr, search));


    }
}

4
5
a. Recursive Binary Search.
import java.util.Scanner;

public class MyRecursiveBinarySearch {

public static int recursiveBinarySearch(int[] sortedArray, int start, int end, int key) {

if (start < end) {

int mid = start + (end - start) / 2;

if (key < sortedArray[mid]) {

return recursiveBinarySearch(sortedArray, start, mid, key);

} else if (key > sortedArray[mid]) {

return recursiveBinarySearch(sortedArray, mid+1, end , key);

} else {

return mid;

// return -(start + 1);

return -1;

public static void main(String[] args) {

Scanner input = new Scanner(System.in);

System.out.print("Enter the size of the array: ");

int size = input.nextInt();

System.out.print("Enter an array of numbers: ");

int[] arr = new int[size];

for(int i=0; i<arr.length; i++)

arr[i]=input.nextInt();

6
}

System.out.print("Enter the number you want to search: ");

int search = input.nextInt();

int pos = recursiveBinarySearch (arr,0, arr.length, search);

if (pos == -1)

System.out.println("Key "+ search + " not found ");

else

System.out.println("Key "+ search+ " position: " + (pos+1) );

2. Bubble Sort

Bubble Sort is the simplest sorting algorithm that works by repeatedly swapping the adjacent elements if
they are in wrong order.

 Worst and Average Case Time Complexity: O(n*n). Worst case occurs when array is reverse
sorted.
 Best Case Time Complexity: O(n). Best case occurs when array is already sorted.
 Auxiliary Space: O(1)
 Boundary Cases: Bubble sort takes minimum time (Order of n) when elements are already
sorted.

public class MyBubbleSort {


 
    // logic to sort the elements
    public static void bubble_srt(int array[]) {
        int n = array.length;
        int k;
        for (int m = n; m >= 0; m--) {
            for (int i = 0; i < n - 1; i++) {
                k = i + 1;
                if (array[i] > array[k]) {
                    swapNumbers(i, k, array);
                }
            }
            printNumbers(array);
        }
    }

7
 
    private static void swapNumbers(int i, int j, int[] array) {
        int temp;
        temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }
 
    private static void printNumbers(int[] input) {
         
        for (int i = 0; i < input.length; i++) {
            System.out.print(input[i] + ", ");
        }
        System.out.println("\n");
    }
 
    public static void main(String[] args) {

Scanner input = new Scanner(System.in);


System.out.print("Enter the size of the array: ");
int size = input.nextInt();
System.out.print("Enter an array of numbers: ");

int[] arr = new int[size];


for(int i=0; i<arr.length; i++)
        {
            arr[i]=input.nextInt();
        }

        bubble_srt(arr);
 
    }
}

8
3. Insertion Sort

Algorithm
// Sort an arr[] of size n
insertionSort(arr, n)
Loop from i = 1 to n-1.
……a) Pick element arr[i] and insert it into sorted sequence arr[0…i-1]

Time Complexity: O(n*n)

import java.util.Scanner;
// Java program for implementation of Insertion Sort
public class InsertionSort
{
// Driver method
public static void main(String args[])
{
Scanner s=new Scanner(System.in);
System.out.println("Enter number of elements");
int n=s.nextInt();
int arr[]=new int[n];
System.out.println("Enter elements");
for(int i=0;i<n;i++){//for reading array
arr[i]=s.nextInt();
}
InsertionSort ob = new InsertionSort();
ob.sort(arr);
printArray(arr);
}
/*Function to sort array using insertion sort*/
void sort(int arr[])
{
int n = arr.length;
for (int i=1; i<n; ++i)
{
int key = arr[i];
int j = i-1;
/* Move elements of arr[0..i-1], that are
greater than key, to one position ahead
of their current position */
while (j>=0 && arr[j] > key)
{
arr[j+1] = arr[j];
j = j-1;
}
arr[j+1] = key;
}
}

9
/* A utility function to print array of size n*/
static void printArray(int arr[])
{
int n = arr.length;
for (int i=0; i<n; ++i)
System.out.print(arr[i] + " ");
System.out.println();
}
}

4. Selection Sort

The selection sort algorithm sorts an array by repeatedly finding the minimum element (considering
ascending order) from unsorted part and putting it at the beginning. The algorithm maintains two
subarrays in a given array.

1) The subarray which is already sorted.


2) Remaining subarray which is unsorted.

In every iteration of selection sort, the minimum element (considering ascending order) from the unsorted
subarray is picked and moved to the sorted subarray.

Program

import java.util.Scanner;
// Java program for implementation of Selection Sort
class SelectionSort
{
void sort(int arr[])
{
int n = arr.length;
// One by one move boundary of unsorted subarray
for (int i = 0; i < n-1; i++)
{
// Find the minimum element in unsorted array
int min_idx = i;
for (int j = i+1; j < n; j++)
if (arr[j] < arr[min_idx])
min_idx = j;
// Swap the found minimum element with the first
// element
int temp = arr[min_idx];
arr[min_idx] = arr[i];
arr[i] = temp;
}
}

10
// Prints the array
void printArray(int arr[])
{
int n = arr.length;
for (int i=0; i<n; ++i)
System.out.print(arr[i]+" ");
System.out.println();
}
// Driver code to test above
public static void main(String args[])
{
Scanner s=new Scanner(System.in);
System.out.println("Enter number of elements");
int n=s.nextInt();
int arr[]=new int[n];
System.out.println("Enter elements");
for(int i=0;i<n;i++){//for reading array
arr[i]=s.nextInt();
}
SelectionSort ob = new SelectionSort();
ob.sort(arr);
System.out.println("Sorted array");
ob.printArray(arr);
}
}

5. Heap Sort

// Java program for implementation of Heap Sort


public class HeapSort
{
    public void sort(int arr[])
    {
        int n = arr.length;
 
        // Build heap (rearrange array)
        for (int i = n / 2 - 1; i >= 0; i--)
            heapify(arr, n, i);
 
        // One by one extract an element from heap
        for (int i=n-1; i>=0; i--)
        {
            // Move current root to end
            int temp = arr[0];
            arr[0] = arr[i];
            arr[i] = temp;
 

11
            // call max heapify on the reduced heap
            heapify(arr, i, 0);
        }
    }
 
    // To heapify a subtree rooted with node i which is
    // an index in arr[]. n is size of heap
    void heapify(int arr[], int n, int i)
    {
        int largest = i;  // Initialize largest as root
        int l = 2*i + 1;  // left = 2*i + 1
        int r = 2*i + 2;  // right = 2*i + 2
 
        // If left child is larger than root
        if (l < n && arr[l] > arr[largest])
            largest = l;
 
        // If right child is larger than largest so far
        if (r < n && arr[r] > arr[largest])
            largest = r;
 
        // If largest is not root
        if (largest != i)
        {
            int swap = arr[i];
            arr[i] = arr[largest];
            arr[largest] = swap;
 
            // Recursively heapify the affected sub-tree
            heapify(arr, n, largest);
        }
    }
 
    /* A utility function to print array of size n */
    static void printArray(int arr[])
    {
        int n = arr.length;
        for (int i=0; i<n; ++i)
            System.out.print(arr[i]+" ");
        System.out.println();
    }
 
    // Driver program
    public static void main(String args[])
    {
        int arr[] = {12, 11, 13, 5, 6, 7};
        int n = arr.length;
 
        HeapSort ob = new HeapSort();
        ob.sort(arr);
 

12
        System.out.println("Sorted array is");
        printArray(arr);
    }
}

6. Merge Sort

Algorithm

1) Divide the unsorted array into n partitions, each partition contains 1 element. Here the one
element is considered as sorted.
2) Repeatedly merge partitioned units to produce new sublists until there is only 1 sublist
remaining. This will be the sorted list at the end.

It divides input array in two halves, calls itself for the two halves and then merges the two sorted
halves. The merge() function is used for merging two halves. The merge(arr, l, m, r) is key process
that assumes that arr[l..m] and arr[m+1..r] are sorted and merges the two sorted sub-arrays into one.
See following C implementation for details.
2) MergeSort(arr[], l, r)
3) If r > l
4) 1. Find the middle point to divide the array into two halves:
5) middle m = (l+r)/2
6) 2. Call mergeSort for first half:
7) Call mergeSort(arr, l, m)
8) 3. Call mergeSort for second half:
9) Call mergeSort(arr, m+1, r)
10) 4. Merge the two halves sorted in step 2 and 3:
11) Call merge(arr, l, m, r)

// Recursive Java Program for merge sort


 
import java.util.Arrays;
public class GFG
{
    public static void mergeSort(int[] array)
    {
        if(array == null)
        {
            return;
        }
 
        if(array.length > 1)
        {
            int mid = array.length / 2;
 
            // Split left part
            int[] left = new int[mid];

13
            for(int i = 0; i < mid; i++)
            {
                left[i] = array[i];
            }
             
            // Split right part
            int[] right = new int[array.length - mid];
            for(int i = mid; i < array.length; i++)
            {
                right[i - mid] = array[i];
            }
            mergeSort(left);
            mergeSort(right);
 
            int i = 0;
            int j = 0;
            int k = 0;
 
            // Merge left and right arrays
            while(i < left.length && j < right.length)
            {
                if(left[i] < right[j])
                {
                    array[k] = left[i];
                    i++;
                }
                else
                {
                    array[k] = right[j];
                    j++;
                }
                k++;
            }
            // Collect remaining elements
            while(i < left.length)
            {
                array[k] = left[i];
                i++;
                k++;
            }
            while(j < right.length)
            {
                array[k] = right[j];
                j++;
                k++;
            }
        }
    }
    // Driver program to test above functions.
    public static void main(String[] args)
    {

14
        int arr[] = {12, 11, 13, 5, 6, 7};
        int i=0;
        System.out.println("Given array is");
 
        for(i=0; i<arr.length; i++)
            System.out.print(arr[i]+" ");
 
        mergeSort(arr);
 
        System.out.println("\n");
        System.out.println("Sorted array is");
 
        for(i=0; i<arr.length; i++)
            System.out.print(arr[i]+" ");
    }
}

15
a. Recursive Bubble Sort
// Java program for recursive implementation
// of Bubble sort
 
import java.util.Arrays;
 
public class GFG
{
    // A function to implement bubble sort
    static void bubbleSort(int arr[], int n)
    {
        // Base case
        if (n == 1)
            return;
      
        // One pass of bubble sort. After
        // this pass, the largest element
        // is moved (or bubbled) to end.
        for (int i=0; i<n-1; i++)
            if (arr[i] > arr[i+1])
            {
                // swap arr[i], arr[i+1]
                int temp = arr[i];
                arr[i] = arr[i+1];
                arr[i+1] = temp;
            }
      
        // Largest element is fixed,
        // recur for remaining array
        bubbleSort(arr, n-1);
    }
     
    // Driver Method
    public static void main(String[] args)
    {
        int arr[] = {64, 34, 25, 12, 22, 11, 90};
      
        bubbleSort(arr, arr.length);
         
        System.out.println("Sorted array : ");
        System.out.println(Arrays.toString(arr));
    }
}

7. Quick Sort.

// Java program for implementation of QuickSort


class QuickSort

16
{
    /* This function takes last element as pivot,
       places the pivot element at its correct
       position in sorted array, and places all
       smaller (smaller than pivot) to left of
       pivot and all greater elements to right
       of pivot */
    int partition(int arr[], int low, int high)
    {
        int pivot = arr[high];
        int i = (low-1); // index of smaller element
        for (int j=low; j<high; j++)
        {
            // If current element is smaller than or
            // equal to pivot
            if (arr[j] <= pivot)
            {
                i++;
 
                // swap arr[i] and arr[j]
                int temp = arr[i];
                arr[i] = arr[j];
                arr[j] = temp;
            }
        }
 
        // swap arr[i+1] and arr[high] (or pivot)
        int temp = arr[i+1];
        arr[i+1] = arr[high];
        arr[high] = temp;
 
        return i+1;
    }
 
 
    /* The main function that implements QuickSort()
      arr[] --> Array to be sorted,
      low  --> Starting index,
      high  --> Ending index */
    void sort(int arr[], int low, int high)
    {
        if (low < high)
        {
            /* pi is partitioning index, arr[pi] is
              now at right place */
            int pi = partition(arr, low, high);
 
            // Recursively sort elements before
            // partition and after partition
            sort(arr, low, pi-1);
            sort(arr, pi+1, high);

17
        }
    }
 
    /* A utility function to print array of size n */
    static void printArray(int arr[])
    {
        int n = arr.length;
        for (int i=0; i<n; ++i)
            System.out.print(arr[i]+" ");
        System.out.println();
    }
 
    // Driver program
    public static void main(String args[])
    {
        int arr[] = {10, 7, 8, 9, 1, 5};
        int n = arr.length;
 
        QuickSort ob = new QuickSort();
        ob.sort(arr, 0, n-1);
 
        System.out.println("sorted array");
        printArray(arr);
    }
}

8. Study of NP-Complete theory.

 There are computational problems that can not be solved by algorithms even with unlimited time. For
example Turing Halting problem (Given a program and an input, whether the program will eventually
halt when run with that input, or will run forever). Alan Turing proved that general algorithm to solve the
halting problem for all possible program-input pairs cannot exist. A key part of the proof is, Turing
machine was used as a mathematical definition of a computer and program.
Status of NP Complete problems is another failure story, NP complete problems are problems whose
status is unknown. No polynomial time algorithm has yet been discovered for any NP complete problem,
nor has anybody yet been able to prove that no polynomial-time algorithm exist for any of them. The
interesting part is, if any one of the NP complete problems can be solved in polynomial time, then all of
them can be solved.
What are NP,  P,  NP-complete and NP-Hard problems?
P is set of problems that can be solved by a deterministic Turing machine in Polynomial time.
NP is set of decision problems that can be solved by a Non-deterministic Turing Machine in Polynomial
time. P is subset of NP (any problem that can be solved by deterministic machine in polynomial time can
also be solved by non-deterministic machine in polynomial time).
Informally, NP is set of decision problems which can be solved by a polynomial time via a “Lucky
Algorithm”, a magical algorithm that always makes a right guess among the given set of choices.
NP-complete problems are the hardest problems in NP set.  A decision problem L is NP-complete if:
1) L is in NP (Any given solution for NP-complete problems can be verified quickly, but there is no
efficient known solution).
2) Every problem in NP is reducible to L in polynomial time (Reduction is defined below).
A problem is NP-Hard if it follows property 2 mentioned above, doesn’t need to follow property 1.
Therefore, NP-Complete set is also a subset of NP-Hard set.

18
Decision vs Optimization Problems
NP-completeness applies to the realm of decision problems.  It was set up this way because it’s easier to
compare the difficulty of decision problems than that of optimization problems.   In reality, though, being
able to solve a decision problem in polynomial time will often permit us to solve the corresponding
optimization problem in polynomial time (using a polynomial number of calls to the decision problem).
So, discussing the difficulty of decision problems is often really equivalent to discussing the difficulty of
optimization problems. .
For example, consider the vertex cover problem (Given a graph, find out the minimum sized vertex set
that covers all edges). It is an optimization problem. Corresponding decision problem is, given undirected
graph G and k, is there a vertex cover of size k?
What is Reduction?
Let L1 and L2 be two decision problems. Suppose algorithm A2 solves L2. That is, if y is an input for
L2 then algorithm A2 will answer Yes or No depending upon whether y belongs to L2 or not.
The idea is to find a transformation from L1 to L2 so that the algorithm A2 can be part of an algorithm
A1 to solve L1.

Learning reduction in general is very important. For example, if we have library functions to solve certain
problem and if we can reduce a new problem to one of the solved problems, we save a lot of time.
Consider the example of a problem where we have to find minimum product path in a given directed
graph where product of path is multiplication of weights of edges along the path. If we have code for
Dijkstra’s algorithm to find shortest path, we can take log of all weights and use Dijkstra’s algorithm to
find the minimum product path rather than writing a fresh code for this new problem.
How to prove that a given problem is NP complete?
From the definition of NP-complete, it appears impossible to prove that a problem L is NP-Complete.  By
definition, it requires us to that show every problem in NP is polynomial time reducible to L.  
Fortunately, there is an alternate way to prove it.   The idea is to take a known NP-Complete problem and
reduce it to L.  If polynomial time reduction is possible, we can prove that L is NP-Complete by
transitivity of reduction (If a NP-Complete problem is reducible to L in polynomial time, then all
problems are reducible to L in polynomial time).
What was the first problem proved as NP-Complete?
There must be some first NP-Complete problem proved by definition of NP-Complete problems.  SAT
(Boolean satisfiability problem) is the first NP-Complete problem proved by Cook (See CLRS book for
proof).
It is always useful to know about NP-Completeness even for engineers. Suppose you are asked to write an
efficient algorithm to solve an extremely important problem for your company. After a lot of thinking,
you can only come up exponential time approach which is impractical. If you don’t know about NP-
Completeness, you can only say that I could not come with an efficient algorithm. If you know about NP-
Completeness and prove that the problem as NP-complete, you can proudly say that the polynomial time
solution is unlikely to exist. If there is a polynomial time solution possible, then that solution solves a big
problem of computer science many scientists have been trying for years.
9. Study of Cook’s theorem.

Stephen Cook presented four theorems in his paper “The Complexity of Theorem Proving Procedures”.
These theorems are stated below. We do understand that many unknown terms are being used in this
chapter, but we don’t have any scope to discuss everything in detail.

Following are the four theorems by Stephen Cook −

19
Theorem-1
If a set S of strings is accepted by some non-deterministic Turing machine within polynomial time,
then S is P-reducible to {DNF tautologies}.

Theorem-2
The following sets are P-reducible to each other in pairs (and hence each has the same polynomial
degree of difficulty): {tautologies}, {DNF tautologies}, D3, {sub-graph pairs}.

Theorem-3
 For any TQ(k) of type Q, TQ(k)k√(logk)2TQ(k)k(logk)2 is unbounded
 There is a TQ(k) of type Q such that TQ(k)⩽2k(logk)2TQ(k)⩽2k(logk)2
Theorem-4
If the set S of strings is accepted by a non-deterministic machine within time T(n) = 2n, and if TQ(k) is an
honest (i.e. real-time countable) function of type Q, then there is a constant K, so S can be recognized by
a deterministic machine within time TQ(K8n).

 First, he emphasized the significance of polynomial time reducibility. It means that if we have a
polynomial time reduction from one problem to another, this ensures that any polynomial time
algorithm from the second problem can be converted into a corresponding polynomial time
algorithm for the first problem.
 Second, he focused attention on the class NP of decision problems that can be solved in
polynomial time by a non-deterministic computer. Most of the intractable problems belong to
this class, NP.
 Third, he proved that one particular problem in NP has the property that every other problem in
NP can be polynomially reduced to it. If the satisfiability problem can be solved with a
polynomial time algorithm, then every problem in NP can also be solved in polynomial time. If
any problem in NP is intractable, then satisfiability problem must be intractable. Thus,
satisfiability problem is the hardest problem in NP.
 Fourth, Cook suggested that other problems in NP might share with the satisfiability problem this
property of being the hardest member of NP.

10. Study of sorting network.

A sorting network is a mathematical model of a network of wires and comparator modules that
is used to sort a sequence of numbers. Each comparator connects two wires and sorts the values
by outputting the smaller value to one wire, and the larger to the other. The main difference
between sorting networks and comparison sorting algorithms is that with a sorting network the

20
sequence of comparisons is set in advance, regardless of the outcome of previous comparisons.
This independence of comparison sequences is useful for parallel execution of the algorithms.
Despite the simplicity of the model, sorting network theory is surprisingly deep and complex.
These networks were first studied circa 1954 by Armstrong, Nelson and O'Connor, who
subsequently patented the idea. Sorting networks can be implemented either in hardware or in
software. Donald Knuth describes how the comparators for binary integers can be implemented
as simple, three-state electronic devices. Batcher, in 1968, suggested using them to construct
switching networks for computer hardware, replacing both buses and the faster, but more
expensive, crossbar switches. Since the 2000s, they are used (especially bitonic mergesort) by
the GPGPU community for constructing sorting algorithms to run on graphics processing units.

21

You might also like