21803013 Vivek Shaurya B12 Week 1 Practice lab – Algorithms and Problem Solving (15B17CI471) We are given an array of n distinct numbers; where n is large numbers are randomly generated. a) Sort the entire array using selection sort, bubble sort, insertion sort, quick sort and merge sort. Print the total number of comparisons done in each of the sorting algorithm. Ans) #include <iostream> using namespace std; //Swap function void swap(int *xp, int *yp) { int temp = *xp; *xp = *yp; *yp = temp; } void selectionSort(int arr[], int n) { int i, j, min_idx; // One by one move boundary of // unsorted subarray for (i = 0; i < n-1; i++) { // Find the minimum element in // unsorted array min_idx = i; for (j = i+1; j < n; j++) if (arr[j] < arr[min_idx]) min_idx = j; // Swap the found minimum element // with the first element if(min_idx!=i) swap(&arr[min_idx], &arr[i]); } } // A function to implement bubble sort void bubbleSort(int arr[], int n) { int i, j; for (i = 0; i < n - 1; i++) // Last i elements are already in place for (j = 0; j < n - i - 1; j++) if (arr[j] > arr[j + 1]) swap(&arr[j], &arr[j + 1]); } void insertionSort(int arr[], int n) { int i, key, j; for (i = 1; i < n; i++) { key = arr[i]; 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; } } int partition(int arr[], int low, int high) { int pivot = arr[high]; // pivot int i = (low - 1); // Index of smaller element and indicates // the right position of pivot found so far for (int j = low; j <= high - 1; j++) { // If current element is smaller than the pivot if (arr[j] < pivot) { i++; // increment index of smaller element swap(&arr[i], &arr[j]); } } swap(&arr[i + 1], &arr[high]); return (i + 1); } void quickSort(int arr[], int low, int high) { if (low < high) { /* pi is partitioning index, arr[p] is now at right place */ int pi = partition(arr, low, high); // Separately sort elements before // partition and after partition quickSort(arr, low, pi - 1); quickSort(arr, pi + 1, high); } } void merge(int array[], int const left, int const mid, int const right) { auto const subArrayOne = mid - left + 1; auto const subArrayTwo = right - mid; // Create temp arrays auto *leftArray = new int[subArrayOne], *rightArray = new int[subArrayTwo]; // Copy data to temp arrays leftArray[] and rightArray[] for (auto i = 0; i < subArrayOne; i++) leftArray[i] = array[left + i]; for (auto j = 0; j < subArrayTwo; j++) rightArray[j] = array[mid + 1 + j]; auto indexOfSubArrayOne = 0, // Initial index of first sub-array indexOfSubArrayTwo = 0; // Initial index of second sub-array int indexOfMergedArray = left; // Initial index of merged array // Merge the temp arrays back into array[left..right] while (indexOfSubArrayOne < subArrayOne && indexOfSubArrayTwo < subArrayTwo) { if (leftArray[indexOfSubArrayOne] <= rightArray[indexOfSubArrayTwo]) { array[indexOfMergedArray] = leftArray[indexOfSubArrayOne]; indexOfSubArrayOne++; } else { array[indexOfMergedArray] = rightArray[indexOfSubArrayTwo]; indexOfSubArrayTwo++; } indexOfMergedArray++; } // Copy the remaining elements of // left[], if there are any while (indexOfSubArrayOne < subArrayOne) { array[indexOfMergedArray] = leftArray[indexOfSubArrayOne]; indexOfSubArrayOne++; indexOfMergedArray++; } // Copy the remaining elements of // right[], if there are any while (indexOfSubArrayTwo < subArrayTwo) { array[indexOfMergedArray] = rightArray[indexOfSubArrayTwo]; indexOfSubArrayTwo++; indexOfMergedArray++; } delete[] leftArray; delete[] rightArray; } // begin is for left index and end is // right index of the sub-array // of arr to be sorted */ void mergeSort(int array[], int const begin, int const end) { if (begin >= end) return; // Returns recursively auto mid = begin + (end - begin) / 2; mergeSort(array, begin, mid); mergeSort(array, mid + 1, end); merge(array, begin, mid, end); } //Function to print an array void printArray(int arr[], int size) { int i; for (i=0; i < size; i++) cout << arr[i] << " "; cout << endl; } // Driver program to test above functions int main() { int arr[] = {64, 25, 12, 22, 11}; int n = sizeof(arr)/sizeof(arr[0]); int input; cout<<"Options:"<<endl; cout<<"1. Selection Sort"<<endl; cout<<"2. Bubble Sort"<<endl; cout<<"3. Insertion Sort"<<endl; cout<<"4. Quick Sort"<<endl; cout<<"5. Merge Sort"<<endl; cin>>input; //selectionSort(arr, n); //bubbleSort(arr,n); //insertionSort(arr,n); //quickSort(arr,0,n-1); //mergeSort(arr,0,n-1); if(input==1) { selectionSort(arr, n); cout << "Sorted array: \n"; printArray(arr, n); } if(input==2) { bubbleSort(arr,n); cout << "Sorted array: \n"; printArray(arr, n); } else if(input==3) { insertionSort(arr,n); cout << "Sorted array: \n"; printArray(arr, n); } else if(input==4) { quickSort(arr,0,n-1); cout << "Sorted array: \n"; printArray(arr, n); } else if(input==5) { mergeSort(arr,0,n-1); cout << "Sorted array: \n"; printArray(arr, n); } else { cout<<"Invalid input"<<endl; } return 0; } Time Complexities:1) Selection Sort: 3.056 s 2) Bubble Sort: 3.797 s 3) Insertion Sort: 2.428 s 4) Quick Sort: 2.214 s 5) Merge Sort: 1.709 s b) Take sorted array from part a) and again run all the sorting algorithm functions. Print the total number of comparisons done in each of the sorting algorithm. Ans) #include <iostream> using namespace std; //Swap function void swap(int *xp, int *yp) { int temp = *xp; *xp = *yp; *yp = temp; } void selectionSort(int arr[], int n) { int i, j, min_idx; // One by one move boundary of // unsorted subarray for (i = 0; i < n-1; i++) { // Find the minimum element in // unsorted array min_idx = i; for (j = i+1; j < n; j++) if (arr[j] < arr[min_idx]) min_idx = j; // Swap the found minimum element // with the first element if(min_idx!=i) swap(&arr[min_idx], &arr[i]); } } // A function to implement bubble sort void bubbleSort(int arr[], int n) { int i, j; for (i = 0; i < n - 1; i++) // Last i elements are already in place for (j = 0; j < n - i - 1; j++) if (arr[j] > arr[j + 1]) swap(&arr[j], &arr[j + 1]); } void insertionSort(int arr[], int n) { int i, key, j; for (i = 1; i < n; i++) { key = arr[i]; 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; } } int partition(int arr[], int low, int high) { int pivot = arr[high]; // pivot int i = (low - 1); // Index of smaller element and indicates // the right position of pivot found so far for (int j = low; j <= high - 1; j++) { // If current element is smaller than the pivot if (arr[j] < pivot) { i++; // increment index of smaller element swap(&arr[i], &arr[j]); } } swap(&arr[i + 1], &arr[high]); return (i + 1); } void quickSort(int arr[], int low, int high) { if (low < high) { /* pi is partitioning index, arr[p] is now at right place */ int pi = partition(arr, low, high); // Separately sort elements before // partition and after partition quickSort(arr, low, pi - 1); quickSort(arr, pi + 1, high); } } void merge(int array[], int const left, int const mid, int const right) { auto const subArrayOne = mid - left + 1; auto const subArrayTwo = right - mid; // Create temp arrays auto *leftArray = new int[subArrayOne], *rightArray = new int[subArrayTwo]; // Copy data to temp arrays leftArray[] and rightArray[] for (auto i = 0; i < subArrayOne; i++) leftArray[i] = array[left + i]; for (auto j = 0; j < subArrayTwo; j++) rightArray[j] = array[mid + 1 + j]; auto indexOfSubArrayOne = 0, // Initial index of first sub-array indexOfSubArrayTwo = 0; // Initial index of second sub-array int indexOfMergedArray = left; // Initial index of merged array // Merge the temp arrays back into array[left..right] while (indexOfSubArrayOne < subArrayOne && indexOfSubArrayTwo < subArrayTwo) { if (leftArray[indexOfSubArrayOne] <= rightArray[indexOfSubArrayTwo]) { array[indexOfMergedArray] = leftArray[indexOfSubArrayOne]; indexOfSubArrayOne++; } else { array[indexOfMergedArray] = rightArray[indexOfSubArrayTwo]; indexOfSubArrayTwo++; } indexOfMergedArray++; } // Copy the remaining elements of // left[], if there are any while (indexOfSubArrayOne < subArrayOne) { array[indexOfMergedArray] = leftArray[indexOfSubArrayOne]; indexOfSubArrayOne++; indexOfMergedArray++; } // Copy the remaining elements of // right[], if there are any while (indexOfSubArrayTwo < subArrayTwo) { array[indexOfMergedArray] = rightArray[indexOfSubArrayTwo]; indexOfSubArrayTwo++; indexOfMergedArray++; } delete[] leftArray; delete[] rightArray; } // begin is for left index and end is // right index of the sub-array // of arr to be sorted */ void mergeSort(int array[], int const begin, int const end) { if (begin >= end) return; // Returns recursively auto mid = begin + (end - begin) / 2; mergeSort(array, begin, mid); mergeSort(array, mid + 1, end); merge(array, begin, mid, end); } //Function to print an array void printArray(int arr[], int size) { int i; for (i=0; i < size; i++) cout << arr[i] << " "; cout << endl; } // Driver program to test above functions int main() { int arr[] = {11,12,22,25,64}; int n = sizeof(arr)/sizeof(arr[0]); int input; cout<<"Options:"<<endl; cout<<"1. Selection Sort"<<endl; cout<<"2. Bubble Sort"<<endl; cout<<"3. Insertion Sort"<<endl; cout<<"4. Quick Sort"<<endl; cout<<"5. Merge Sort"<<endl; cin>>input; //selectionSort(arr, n); //bubbleSort(arr,n); //insertionSort(arr,n); //quickSort(arr,0,n-1); //mergeSort(arr,0,n-1); if(input==1) { selectionSort(arr, n); cout << "Sorted array: \n"; printArray(arr, n); } if(input==2) { bubbleSort(arr,n); cout << "Sorted array: \n"; printArray(arr, n); } else if(input==3) { insertionSort(arr,n); cout << "Sorted array: \n"; printArray(arr, n); } else if(input==4) { quickSort(arr,0,n-1); cout << "Sorted array: \n"; printArray(arr, n); } else if(input==5) { mergeSort(arr,0,n-1); cout << "Sorted array: \n"; printArray(arr, n); } else { cout<<"Invalid input"<<endl; } return 0; } Time Complexities:1) Selection Sort: 2.536 s 2) Bubble Sort: 1.664 s 3) Insertion Sort: 1.461s 4) Quick Sort: 2.547 s 5) Merge Sort: 2.460s c) Change the functions to take a flag “order” as argument. This order can be „d‟ or „a‟ for descending and ascending respectively. The function will sort the array in descending and ascending order depending on the flag value. Ans) #include<iostream> using namespace std; int main() { int i, j, size, temp; int arr[25]; // Asking for input cout << "Enter the total no. of elements: "; cin >> size; // Enter the elements cout << "Enter the elements of the array: " << endl; for (i = 0; i < size; i++){ cin >> arr[i]; } cout<<"Enter d for descending or a for ascending"<<endl; char opt; cin>>opt; if(opt=='a') { // Sorting elements in ascending order for (i = 0; i < size; i++){ for (j = i; j < size; j++){ if (arr[i] > arr[j+1]){ temp = arr[i]; arr[i] = arr[j+1]; arr[j+1] = temp; } } } // Displaying output cout << "Elements sorted in the ascending order are: " << endl; for (i = 1; i <= size; i++){ cout << arr[i] << " "; } } else if(opt=='d') { //sorting - Descending ORDER for(i=0;i<size;i++) { for(j=i+1;j<size;j++) { if(arr[i]<arr[j]) { temp =arr[i]; arr[i]=arr[j]; arr[j]=temp; } } } //print sorted array elements cout<<"Sorted (Descending Order) Array elements:"<<endl; for(i=0;i<size;i++) cout<<arr[i]<<" "; cout<<endl; } else{ cout<<"Invalid output"<<endl; } return 0; } d) Take sorted array from part a). Again run all the sorting algorithm functions with the “order” flag changed. If array is already in ascending order pass flag value as „d‟ and vice versa. Print the total number of comparisons done in each of the sorting algorithm. Ans) #include <iostream> using namespace std; int count1=0; int count2=0; int count3=0; int count4=0; int count5=0; //Swap function void swap(int *xp, int *yp) { int temp = *xp; *xp = *yp; *yp = temp; } void selectionSort(int arr[], int n) { int i, j, min_idx,count1=0;; // One by one move boundary of // unsorted subarray for (i = 0; i < n-1; i++) { // Find the minimum element in // unsorted array min_idx = i; for (j = i+1; j < n; j++) if (arr[j] < arr[min_idx]) min_idx = j; // Swap the found minimum element // with the first element if(min_idx!=i) swap(&arr[min_idx], &arr[i]); count1++; } cout << "No of comparisons: \n"<<count1; } // A function to implement bubble sort void bubbleSort(int arr[], int n) { int i, j; for (i = 0; i < n - 1; i++) // Last i elements are already in place for (j = 0; j < n - i - 1; j++) if (arr[j] > arr[j + 1]) swap(&arr[j], &arr[j + 1]); count2++; cout<<"No of comparisons in bubble sort"<<endl<<count2<<endl; } void insertionSort(int arr[], int n) { int i, key, j,count3=0; for (i = 1; i < n; i++) { key = arr[i]; j = i - 1; count3++; // 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; } cout<<"No of comparisons in insertion sort"<<endl<<count3<<endl; } int partition(int arr[], int low, int high) { int count4=0; int pivot = arr[high]; // pivot int i= (low- 1); // Index of smaller element and indicates // the right position of pivot found so far for (int j = low; j <= high - 1; j++) { count4++; // If current element is smaller than the pivot if (arr[j] < pivot) { i++; // increment index of smaller element swap(&arr[i], &arr[j]); count4++; } } swap(&arr[i + 1], &arr[high]); cout<<"No of comparisons in quick sort"<<endl<<count4<<endl; return (i + 1); } void quickSort(int arr[], int low, int high) { if (low < high) { /* pi is partitioning index, arr[p] is now at right place */ int pi = partition(arr, low, high); // Separately sort elements before // partition and after partition quickSort(arr, low, pi - 1); quickSort(arr, pi + 1, high); } } void merge(int array[], int const left, int const mid, int const right) { int count5=0; auto const subArrayOne = mid - left + 1; auto const subArrayTwo = right - mid; // Create temp arrays auto *leftArray = new int[subArrayOne], *rightArray = new int[subArrayTwo]; // Copy data to temp arrays leftArray[] and rightArray[] for (auto i = 0; i < subArrayOne; i++) leftArray[i] = array[left + i]; for (auto j = 0; j < subArrayTwo; j++) rightArray[j] = array[mid + 1 + j]; auto indexOfSubArrayOne = 0, // Initial index of first sub-array indexOfSubArrayTwo = 0; // Initial index of second sub-array int indexOfMergedArray = left; // Initial index of merged array // Merge the temp arrays back into array[left..right] while (indexOfSubArrayOne < subArrayOne && indexOfSubArrayTwo < subArrayTwo) { if (leftArray[indexOfSubArrayOne] <= rightArray[indexOfSubArrayTwo]) { array[indexOfMergedArray] = leftArray[indexOfSubArrayOne]; indexOfSubArrayOne++; count5++; } else { array[indexOfMergedArray] = rightArray[indexOfSubArrayTwo]; indexOfSubArrayTwo++; } indexOfMergedArray++; } // Copy the remaining elements of // left[], if there are any while (indexOfSubArrayOne < subArrayOne) { array[indexOfMergedArray] = leftArray[indexOfSubArrayOne]; indexOfSubArrayOne++; indexOfMergedArray++; } // Copy the remaining elements of // right[], if there are any while (indexOfSubArrayTwo < subArrayTwo) { array[indexOfMergedArray] = rightArray[indexOfSubArrayTwo]; indexOfSubArrayTwo++; indexOfMergedArray++; } cout<<"No of comparisons in merge sort"<<endl<<count5<<endl; delete[] leftArray; delete[] rightArray; } // begin is for left index and end is // right index of the sub-array // of arr to be sorted */ void mergeSort(int array[], int const begin, int const end) { if (begin >= end) return; // Returns recursively auto mid = begin + (end - begin) / 2; mergeSort(array, begin, mid); mergeSort(array, mid + 1, end); merge(array, begin, mid, end); } bool Check_is_sorted(int arr[],int n) { bool flag=true; int z=arr[1]-arr[0]; if(z>0) { for(int i=1;i<n;i++) { if(arr[i]>arr[i+1]) { flag=false; break; } } } else { for(int i=1;i<n;i++) { if(arr[i]<arr[i+1]) { flag=false; break; } } } cout<<"a"<<endl; return flag; } bool Check_is_desc_sorted(int arr[],int n) { bool flag=true; int z=arr[1]-arr[0]; if(z>0) { for(int i=1;i<n;i++) { if(arr[i]<arr[i+1]) { flag=false; break; } } } else { for(int i=1;i<n;i++) { if(arr[i]>arr[i+1]) { flag=false; break; } } } cout<<"d"<<endl; return flag; } //Function to print an array void printArray(int arr[], int size) { int i; for (i=0; i < size; i++) cout << arr[i] << " "; cout << endl; } // Driver program to test above functions int main() { int arr[] = {11,12,22,25,64}; int n = sizeof(arr)/sizeof(arr[0]); int count=0; /*int input; cout<<"Options:"<<endl; cout<<"1. Selection Sort"<<endl; cout<<"2. Bubble Sort"<<endl; cout<<"3. Insertion Sort"<<endl; cout<<"4. Quick Sort"<<endl; cout<<"5. Merge Sort"<<endl; cout<<"6. Comparisons in Selection Sort"<<endl; cout<<"7. Comparisons in Bubble Sort"<<endl; cout<<"8. Comparisons in Insertion Sort"<<endl; cout<<"9. Comparisons in Quick Sort"<<endl; cout<<"10.Comparisons in Merge Sort"<<endl; cin>>input;*/ //selectionSort(arr, n); //bubbleSort(arr,n); //insertionSort(arr,n); //quickSort(arr,0,n-1); //mergeSort(arr,0,n-1); Check_is_sorted(arr,n); //Check_is_desc_sorted(arr,n); /*if(input==1) { selectionSort(arr, n); cout << "Sorted array: \n"; printArray(arr, n); } else if(input==2) { bubbleSort(arr,n); cout << "Sorted array: \n"; printArray(arr, n); } else if(input==3) { insertionSort(arr,n); cout << "Sorted array: \n"; printArray(arr, n); } else if(input==4) { quickSort(arr,0,n-1); cout << "Sorted array: \n"; printArray(arr, n); } else if(input==5) { mergeSort(arr,0,n-1); cout << "Sorted array: \n"; printArray(arr, n); } else if(input==6) { selectionSort(arr, n); } else if(input==7) { bubbleSort(arr, n); } else if(input==8) { insertionSort(arr, n); } else if(input==9) { quickSort(arr,0,n-1); } else if(input==10) { mergeSort(arr,0,n-1); cout<<"No of comparisons in merge sort"<<endl<<count5<<endl; } else { cout<<"Invalid input"<<endl; }*/ return 0; } e) Implement quick sort with three overloaded functions. 1st taking pivot as first index, 2nd taking pivot as last index and 3rd taking pivot as middle index. Ans) def quick_sort(arr, low, high, pivot_index): if low < high: pivot = arr[pivot_index] i = low j = high while i <= j: while arr[i] < pivot: i += 1 while arr[j] > pivot: j -= 1 if i <= j: arr[i], arr[j] = arr[j], arr[i] i += 1 j -= 1 if low < j: quick_sort(arr, low, j, pivot_index) if i < high: quick_sort(arr, i, high, pivot_index) return arr def quick_sort_first(arr): return quick_sort(arr, 0, len(arr)-1, 0) def quick_sort_last(arr): return quick_sort(arr, 0, len(arr)-1, len(arr)-1) def quick_sort_middle(arr): return quick_sort(arr, 0, len(arr)-1, len(arr)//2) f) Analyse complexity of quick sort and write down your observation of best and worst case. Ans) Best Case Complexity: O (n*logn) Worst case complexity: O(n2) g) Analyse complexity of merge sort and write down your observation of best and worst Case Ans) Best Case Complexity: O (n*logn) Worst case complexity: O (n*logn) h) Analyse complexity of bubble sort and write down your observation of best and worst Case Ans) Best Case Complexity: O (n) Worst case complexity: O (n2) i) Analyse complexity of selection sort and write down your observation of best and worst case. Ans) Best Case Complexity: O (n2) Worst case complexity: O (n2) j) Analyse complexity of insertion sort and write down your observation of best and worst case. Ans) Best Case Complexity: O (n) Worst case complexity: O (n2) k) Write a function to sort all even-placed numbers in increasing and odd-place numbers in decreasing order. The modified array should contain all sorted even-placed numbers followed by reverse sorted odd-placed numbers. Analyse the complexity of your implemented approach Note that the first element is considered as even because of its index 0. Example for part k): Input: arr[] = {0, 1, 2, 3, 4, 5, 6, 7} Output: arr[] = {0, 2, 4, 6, 7, 5, 3, 1} Even-place elements : 0, 2, 4, 6 Odd-place elements : 1, 3, 5, 7 Even-place elements in increasing order : 0, 2, 4, 6 Odd-Place elements in decreasing order : 7, 5, 3, 1 Input: arr[] = {3, 1, 2, 4, 5, 9, 13, 14, 12} Output: 1, 2, 4, 6, 7, 5, 3, 1 Even-place elements : 3, 2, 5, 13, 12 Odd-place elements : 1, 4, 9, 14 Even-place elements in increasing order : 2, 3, 5, 12, 13 Odd-Place elements in decreasing order : 14, 9, 4, 1 Ans) #include <bits/stdc++.h> using namespace std; void bitonicGenerator(int arr[], int n) { // create evenArr[] and oddArr[] vector<int> evenArr; vector<int> oddArr; // Put elements in oddArr[] and evenArr[] as // per their position for (int i = 0; i < n; i++) { if (!(i % 2)) evenArr.push_back(arr[i]); else oddArr.push_back(arr[i]); } // sort evenArr[] in ascending order // sort oddArr[] in descending order sort(evenArr.begin(), evenArr.end()); sort(oddArr.begin(), oddArr.end(), greater<int>()); int i = 0; for (int j = 0; j < evenArr.size(); j++) arr[i++] = evenArr[j]; for (int j = 0; j < oddArr.size(); j++) arr[i++] = oddArr[j]; } // Driver Program int main() { int arr[] = {0, 1, 2, 3, 4, 5, 6, 7}; int n = sizeof(arr) / sizeof(arr[0]); bitonicGenerator(arr, n); for (int i = 0; i < n; i++) cout << arr[i] << " "; return 0; } Time Complexity: O(n Log n) Auxiliary Space: O(1) l) Given an integer array of which both first half and second half are sorted. Task is to merge two sorted halves of array into single sorted array. Analyse the complexity of your implemented approach Example: Input : A[] = { 2 ,3 , 8 ,-1 ,7 ,10 } Output : -1 , 2 , 3 , 7 , 8 , 10 Input : A[] = {-4 , 6, 9 , -1 , 3 } Output : -4 , -1 , 3 , 6 , 9 Ans) #include <bits/stdc++.h> using namespace std; void mergeTwoHalf(int A[], int n) { // Sort the given array using sort STL sort(A, A + n); } // Driver code int main() { int A[] = { 2, 3, 8, -1, 7, 10 }; int n = sizeof(A) / sizeof(A[0]); mergeTwoHalf(A, n); // Print sorted Array for (int i = 0; i < n; i++) cout << A[i] << " "; return 0; } Time Complexity: 1) Best Case Complexity: O (n*logn) 2) Worst case complexity: O(n2)