Algo 1
Algo 1
Analysis:
Selection Sort is a simple sorting algorithm that works by repeatedly selecting the minimum (or maximum) element from the
unsorted part of the array and placing it at the beginning of the sorted portion. It has a time complexity of O(n^2), making it
inefficient for large datasets. This assignment aims to help you understand the algorithm and its implementation.
std::swap(arr[i], arr[minIndex]);
}
}
int main()
{
int n;
cout<<"Enter size of array :";
cin>>n;
int arr[n+5];
cout << "Original array: ";
for (int i = 0; i < n; ++i)
{
cin>>arr[i];
}
selectionSort(arr, n);
return 0;
}
Sample input output:
Enter size of array :10
Original array: 5 6 2 1 44 15 36 2 5 1
Sorted array: 1 1 2 2 5 5 6 15 36 44
Remarks:
- In this assignment, you are required to understand the Selection Sort algorithm, write its pseudo code, implement it in C++,
and observe its working on a sample array.
- Pay attention to the nested loops in the algorithm as they contribute to the quadratic time complexity.
- Selection Sort is not recommended for large datasets due to its inefficiency, but it's a good starting point to learn about
sorting algorithms.
2.Problem Name: Sorting an Array using Insertion Sort
Analysis: Insertion Sort is a simple sorting algorithm that builds the sorted array one item at a time. It iterates through the
input array and, at each step, shifts the current element to its correct position within the sorted part of the array. While it's not
the most efficient sorting algorithm for large datasets, its average and best-case time complexity of O(n^2) makes it suitable for
small datasets or nearly sorted data.
Algorithm (Pseudo Code):
procedure insertionSort(A : list of sortable items)
n = length(A)
for i = 1 to n - 1
key = A[i]
j=i-1
while j >= 0 and A[j] > key
A[j + 1] = A[j]
j=j-1
A[j + 1] = key
end procedure
Sample code:
#include<bits/stdc++.h>
using namespace std;
#include <iostream>
arr[j + 1] = currentElement;
}
}
int main()
{
int n;
cout<<"Enter size of array :";
cin>>n;
int arr[n+5];
cout << "Original array: ";
for (int i = 0; i < n; ++i)
{
cin>>arr[i];
}
insertionSort(arr, n);
return 0;
}
Sample input output:
Enter size of array :5
Original array: 124 39 46 57 12
Sorted array: 12 39 46 57 124
Remarks: Insertion Sort works by iteratively placing each element in its correct position within the sorted part of the array.
While it's not suitable for large datasets due to its quadratic time complexity, it's an efficient algorithm for small datasets or
nearly sorted data. Understanding Insertion Sort is crucial for building a foundation in sorting algorithms. For larger datasets,
more efficient algorithms like Merge Sort, Quick Sort, or built-in sorting functions should be used.
3.Problem Name: Sorting an Array using Bubble Sort
Analysis:
Bubble Sort is a simple comparison-based sorting algorithm. It repeatedly steps through the list, compares adjacent elements,
and swaps them if they are in the wrong order. This process continues until the entire array is sorted. Bubble Sort is not
efficient for large datasets due to its quadratic time complexity, but it's easy to understand and can be useful for educational
purposes.
int main()
{
int n;
cout<<"Enter size of array :";
cin>>n;
int arr[n+5];
cout << "Original array: ";
for (int i = 0; i < n; ++i)
{
cin>>arr[i];
}
bubbleSort(arr, n);
return 0;
}
Remarks:
Bubble Sort is a simple sorting algorithm that repeatedly compares adjacent elements and swaps them if they are in the wrong
order. While it's easy to understand and implement, its quadratic time complexity makes it inefficient for larger datasets.
Bubble Sort is generally not recommended for practical use, except for educational purposes or very small datasets. Other
more efficient sorting algorithms like Merge Sort or Quick Sort are preferred for larger datasets.
4.Problem Name: Sorting an Array using Quick Sort
Analysis:
Quick Sort is an efficient and widely used sorting algorithm. It follows the divide-and-conquer strategy. It works by selecting a
pivot element and partitioning the array into two sub-arrays: elements less than the pivot and elements greater than the pivot.
This process is recursively applied to the sub-arrays until the entire array is sorted. Quick Sort has an average-case time
complexity of O(n log n), making it one of the fastest sorting algorithms.
Algorithm (Pseudo Code):
procedure quickSort(A : list of sortable items, low, high)
if low < high
pivotIndex = partition(A, low, high)
quickSort(A, low, pivotIndex - 1)
quickSort(A, pivotIndex + 1, high)
end procedure
int main()
{
int n;
cout<<"Enter size of array :";
cin>>n;
int arr[n+5];
cout << "Original array: ";
for (int i = 0; i < n; ++i)
{
cin>>arr[i];
}
quickSort(arr, 0, n - 1);
cout << "Sorted array: ";
for (int i = 0; i < n; ++i)
{
cout << arr[i] << " ";
}
return 0;
}
Sample Input/Output:
Enter size of array :7
Original array: 50 34 5 61 23 54 23
Sorted array: 5 23 23 34 50 54 61
Remarks:
Quick Sort is a highly efficient sorting algorithm that efficiently sorts arrays by dividing them into sub-arrays and recursively
sorting them. It's widely used in practice due to its average-case time complexity of O(n log n). It can degrade to O(n^2) in the
worst case, but its performance is generally very good. It's considered one of the fastest sorting algorithms for larger datasets.
5.Problem Name:Sorting an Array using Merge Sort
Analysis:Merge Sort is a widely used sorting algorithm known for its efficiency and stability. It follows the divide-and-conquer
strategy by dividing the array into two halves, recursively sorting each half, and then merging the sorted halves back together.
Merge Sort has a consistent time complexity of O(n log n), making it efficient for sorting large datasets.
Algorithm:
procedure mergeSort(A : list of sortable items, low, high)
if low < high
mid = (low + high) / 2
mergeSort(A, low, mid)
mergeSort(A, mid + 1, high)
merge(A, low, mid, high)
end procedure
for i = 0 to n1 - 1
L[i] = A[low + i]
for j = 0 to n2 - 1
R[j] = A[mid + 1 + j]
i=0
j=0
k = low
while i < n1
A[k] = L[i]
i=i+1
k=k+1
while j < n2
A[k] = R[j]
j=j+1
k=k+1
end procedure
Source code:
#include<bits/stdc++.h>
using namespace std;
#include <iostream>
void merge(int arr[], int low, int mid, int high)
{
int n1 = mid - low + 1;
int n2 = high - mid;
int i = 0, j = 0, k = low;
int main()
{
int n;
cout<<"Enter size of array :";
cin>>n;
int arr[n+5];
cout << "Original array: ";
for (int i = 0; i < n; ++i)
{
cin>>arr[i];
}
mergeSort(arr, 0, n - 1);
cout << "Sorted array: ";
for (int i = 0; i < n; ++i)
{
cout << arr[i] << " ";
}
return 0;
}
Sample input-output:
Enter size of array :8
Original array: 12 34 62 54 12 33 85 10
Sorted array: 10 12 12 33 34 54 62 85
Remarks:
Merge Sort efficiently sorts arrays by dividing them into sub-arrays, sorting the sub-arrays, and then merging them back
together. It has a consistent time complexity of O(n log n), making it one of the most efficient sorting algorithms. Merge Sort is
often used in situations where stability and consistent performance are required. It's also used as the basis for other sorting
algorithms, such as Tim Sort.
MaxHeapify(arr, i)
L = left(i)
R = right(i)
if L ≤ heap_size[arr] and arr[L] > arr[i]
largest = L
else
largest = i
if R ≤ heap_size[arr] and arr[R] > arr[largest]
largest = R
if largest ≠ i
swap arr[i] with arr[largest]
MaxHeapify(arr, largest)
End
Source Code:
#include<iostream>
using namespace std;
void MaxHeapify(int A[], int n, int i) {
int largest = i;
int l = 2 * i;
int r = 2 * i + 1;
if (l <= n && A[l] > A[largest]) {
largest = l;}
if (r <= n && A[r] > A[largest]) {
largest = r; }
if (largest != i) {
swap(A[largest], A[i]);
MaxHeapify(A, n, largest);}}
void heapSort(int A[], int n) {
for (int i = n / 2; i >= 1; i--)
MaxHeapify(A, n, i);
for (int i = n; i >1; i--) {
swap(A[1], A[i]);
MaxHeapify(A, i - 1, 1); }}
void printArray(int A[], int n) {
for (int i = 1; i <= n; ++i)
cout << A[i] << " ";
cout << "\n";}
int main() {
int n;
cout << "Enter the number of elements: ";
cin >> n;
int A[n + 1];
cout << "Enter " << n << " elements:\n";
for (int i = 1; i <= n; i++)
cin >> A[i];
cout << "Original array:\n";
printArray(A, n);
heapSort(A, n);
cout << "Sorted array:\n";
printArray(A, n);
return 0; }
Sample Input:
4
35 21 95 17
Sample Output:
Before sorting: 35 21 95 17
After sorting: 17 21 35 95
Remarks: Heap Sort has a consistent time complexity of O(n log n), making it efficient for most scenarios. It is an in-place
sorting algorithm that doesn't require additional memory space. Heaps generally take more time to compute.
8.Problem Name: Solving the Tower of Hanoi Puzzle
Analysis:
The Tower of Hanoi is a classic problem in computer science and mathematics. It involves moving a stack of discs from one peg
to another, with the constraint that only one disc can be moved at a time, and a larger disc cannot be placed on top of a smaller
disc. The problem can be solved recursively, and the number of moves required follows the pattern 2^n - 1, where n is the
number of discs.
Algorithm (Pseudo Code):
procedure towerOfHanoi(n, source, auxiliary, destination)
if n > 0
towerOfHanoi(n-1, source, destination, auxiliary)
move disc from source to destination
towerOfHanoi(n-1, auxiliary, source, destination)
end procedure
Source Code (C++):
#include <iostream>
using namespace std;
void towerOfHanoi(int n, char source, char auxiliary, char destination)
{
if (n > 0)
{
towerOfHanoi(n - 1, source, destination, auxiliary);
std::cout << "Move disc " << n << " from " << source << " to " << destination << std::endl;
towerOfHanoi(n - 1, auxiliary, source, destination);
}
}
int main()
{
int n = 3; // Number of discs
towerOfHanoi(n, 'A', 'B', 'C');
return 0;
}
Sample Input/Output:
Input: Number of discs = 3
Output:
Move disc 1 from A to C
Move disc 2 from A to B
Move disc 1 from C to B
Move disc 3 from A to C
Move disc 1 from B to A
Move disc 2 from B to C
Move disc 1 from A to C
Remarks:
The Tower of Hanoi problem is a classic example of recursive problem-solving. The algorithm uses the concept of breaking a
larger problem into smaller sub-problems and solving them recursively. The number of moves required to solve the Tower of
Hanoi puzzle for n discs is 2^n - 1, making it an interesting problem for exploring the power of recursion in algorithms.
9.Problem Name: Exploring Graph using Breadth-First Search
Analysis: Breadth-First Search (BFS) is a graph traversal algorithm that explores all the vertices of a graph in breadth-first order.
It starts from a given source vertex, visits all its neighbors, then moves on to the neighbors' neighbors, and so on, until all
reachable vertices are visited. BFS is typically used for finding the shortest path in unweighted graphs and for exploring graphs
level by level.
Algorithm (Pseudo Code):
procedure BFS(graph, start)
create an empty queue Q
enqueue start into Q
create a set visited and add start to it
class Graph
{
int vertices;
std::list<int> *adjacencyList;
public:
Graph(int v);
void addEdge(int v, int w);
void BFS(int start);
};
Graph::Graph(int v)
{
vertices = v;
adjacencyList = new std::list<int>[v];
}
visited[start] = true;
q.push(start);
while (!q.empty())
{
int current = q.front();
q.pop();
std::cout << current << " ";
for (int neighbor : adjacencyList[current])
{
if (!visited[neighbor])
{
visited[neighbor] = true;
q.push(neighbor);
}
}
}
}
int main()
{
cout<<"Number of edges :";
int n;
cin>>n;
Graph g(n);
cout<<"enter edges:\n";
for(int i=1;i<n;i++)
{
int a,b;
cin>>a>>b;
g.addEdge(a,b);
}
std::cout << "BFS traversal starting from vertex 0: ";
g.BFS(0);
return 0;
}
Sample Input/Output:
Number of edges :7
enter edges:
01
02
13
14
25
26
BFS traversal starting from vertex 0: 0 1 2 3 4 5 6
Remarks: Breadth-First Search is a versatile algorithm used for traversing and exploring graphs. It's helpful in finding the
shortest path in unweighted graphs and is often employed in solving maze problems and analyzing network connectivity. BFS
uses a queue data structure to ensure that nodes at the same level are visited before moving to the next level.
10.Problem name: Implement Depth-First Search (DFS)
Analysis:Depth-First Search is a graph traversal algorithm that explores as far as possible along each branch before
backtracking. It uses a stack (or recursion) to keep track of the vertices to be explored and explores one branch as deeply as
possible before moving to the next branch.
Algorithm:
Step 1: SET STATUS = 1 (ready state) for each node in G
Step 2:Push the starting node A on the stack and set its STATUS = 2 (waiting state)
Step 3: Repeat Steps 4 and 5 until STACK is empty
Step 4:Pop the top node N. Process it and set its STATUS = 3 (processed state)
Step 5: Push on the stack all the neighbors of N that are in the ready state (whose STATUS = 1) and set their STATUS = 2 (waiting
state)
[END OF LOOP]
Step 6: EXIT
Source Code:
#include <iostream>
#include <vector>
#include <stack>
using namespace std;
int main() {
int vertices, edges;
cout << "Enter the number of vertices: ";
cin >> vertices;
cout << "Enter the number of edges: ";
cin >> edges;
vector<vector<int>> graph(vertices);
cout << "Enter the edges (vertex pairs):" << endl;
for (int i = 0; i < edges; i++) {
int u, v;
cin >> u >> v;
graph[u].push_back(v);
graph[v].push_back(u); }
int start_vertex;
cout << "Enter the start vertex: ";
cin >> start_vertex;
cout << "DFS traversal starting from vertex " << start_vertex << ": ";
dfs(graph, start_vertex);
cout << endl;
return 0;}
Sample Input:
5
6
01
02
13
14
24
34
0
Sample Output:
DFS traversal starting from vertex 0: 0 2 4 1 3
Remarks:The DFS algorithm explores the depth of the graph before backtracking. The order of traversal depends on the order
in which neighbors are pushed onto the stack.
The time complexity of DFS is O(V + E), where V is the number of vertices and E is the number of edges.
11.Problem Name:Searching for an Element in an Array using Linear Search
Analysis:
Linear Search is a simple searching algorithm that sequentially checks each element in a list until a match is found or the whole
list has been searched. It's a straightforward method suitable for small lists or unsorted arrays. However, for larger datasets,
more efficient search algorithms like Binary Search are preferred.
Algorithm:
1. Start from the first element of the array.
2. Compare the target element with the current element.
3. If they match, return the current index.
4. If not, move to the next element and repeat step 2.
5. If the end of the array is reached without a match, return -1 to indicate the element was not found.
Source Code :
#include<bits/stdc++.h>
using namespace std;
#include <iostream>
#include <vector>
int main()
{
int n;
cout<<"Enter size of array :";
cin>>n;
vector<int>arr(n+5,0);
cout << "Original array: ";
for (int i = 0; i <n; ++i)
{
cin>>arr[i];
}
int target;
cout<<" enter target element:";
cin>>target;
if (index != -1)
{
std::cout << "Element " << target << " found at index " << index+1 << std::endl;
}
else
{
std::cout << "Element " << target << " not found in the array" << std::endl;
}
return 0;
}
Sample Input/Output:
Enter size of array :7
Original array: 64 34 25 12 22 11 90
enter target element:22
Element 22 found at index 5
Remarks:
Linear Search is a basic searching algorithm that works well for small datasets or when the elements are not sorted. However,
its time complexity is O(n), making it inefficient for large datasets. It's commonly used when there are no assumptions about
the order of elements or when the dataset is not large enough to justify the implementation of more complex search
algorithms.
12.Problem Name:Implement Matrix Multiplication
Analysis:
Matrix multiplication is the process of multiplying two matrices to obtain a new matrix. It involves taking the dot product of
rows and columns of the matrices to compute the elements of the resulting matrix.
Algorithm:
MATRIX MULTIPLICATION(A, B)
Step 1: Initialize result matrix C with dimensions rows_A x cols_B
Step 2: For each row i in A
a. For each column j in B
i. Initialize sum = 0
ii. For k = 0 to cols_A - 1
A. sum += A[i][k] * B[k][j]
iii. Set C[i][j] = sum
[END OF LOOP]
Step 3: Return matrix C
Source Code:
#include<bits/stdc++.h>
#include <iostream>
#include <vector>
using namespace std;
return C;
}
int main()
{
int rows_A, cols_A, rows_B, cols_B;
cout << "Enter the dimensions of matrix A (rows cols): ";
cin >> rows_A >> cols_A;
cout << "Enter the dimensions of matrix B (rows cols): ";
cin >> rows_B >> cols_B;
if (cols_A != rows_B)
{
cout << "Matrix multiplication is not possible due to incompatible dimensions." << endl;
return 1;
}
vector<vector<int>> A(rows_A, vector<int>(cols_A));
vector<vector<int>> B(rows_B, vector<int>(cols_B));
cout << "Enter the elements of matrix A:" << endl;
for (int i = 0; i < rows_A; i++)
for (int j = 0; j < cols_A; j++)
cin >> A[i][j];
cout << "Enter the elements of matrix B:" << endl;
for (int i = 0; i < rows_B; i++)
for (int j = 0; j < cols_B; j++)
cin >> B[i][j];
vector<vector<int>> result = matrixMultiplication(A, B);
cout << "Resultant matrix C:" << endl;
for (int i = 0; i < rows_A; i++)
{
for (int j = 0; j < cols_B; j++)
{
cout << result[i][j] << " ";
}
cout << endl;
}
return 0;
}
Sample Input:
32
24
46
68
23
135
246
Sample Output:
Resultant matrix C:
10 20 30
20 40 60
30 60 90
Remarks:Matrix multiplication is a fundamental operation in linear algebra. It's important to ensure that the number of
columns in the first matrix matches the number of rows in the second matrix for the operation to be valid.
Compare the middle element of the search space with the key.
If the key is found at middle element, the process is terminated.
If the key is not found at middle element, choose which half will be used as the next search space.
If the key is smaller than the middle element, then the left side is used for next search.
If the key is larger than the middle element, then the right side is used for next search.
This process is continued until the key is found or the total search space is exhausted.
Algorithm:
BINARY SEARCH[A,N,KEY]
Step 1: begin
Step 2: [Initilization]
Lb=1; ub=n;
Step 3: [Search for the ITEM]
Repeat through step 4,while Lower bound is less than Upper Bound.
Step 4: [Obtain the index of middle value]
MID=(lb+ub)/2
Step 5: [Compare to search for ITEM]
If Key<A[MID] then
Ub=MID-1
Other wise if Key >A[MID] then
Lb=MID+1
Otherwise write “Match Found”
Return Middle.
Step 6: [Unsuccessful Search]
write “Match Not Found”
Step 7: Stop.
Source Code:
#include <iostream>
using namespace std;
Remarks:Binary Search is highly efficient for sorted arrays, with a time complexity of O(log n), where n is the number of
elements. Its divide-and-conquer approach makes it an invaluable tool for quick and precise element retrieval.
14.Problem Name:Finding the Convex Hull of a Set of Points using Quick Hull
Analysis:
The Convex Hull of a set of points is the smallest convex polygon that encloses all the points. Quick Hull is an efficient algorithm
to compute the convex hull of a set of points in a 2D plane. It works by recursively finding the convex hull of the points on the
left and right of the line connecting the two extreme points. Quick Hull has an average-case time complexity of O(n log n),
making it efficient for large sets of points.
Algorithm:
procedure quickHull(points)
if number of points <= 1
return points
divide points into two sets: leftSet (above leftmost-rightmost line) and rightSet (below rightmost-leftmost line)
return convexHull
end procedure
select the point with the maximum distance from the line p1-p2 as p
add p to convexHull
divide points into two sets: upperSet (above p1-p line) and lowerSet (below p1-p line)
findHull(upperSet, p1, p)
findHull(lowerSet, p, p2)
end procedure
Source code:
#include <iostream>
#include <vector>
#include <algorithm>
struct Point {
int x, y;
};
// Find the point with the maximum distance from the line
for (int i = 0; i < points.size(); i++) {
int currDist = findDistance(p1, p2) - findDistance(p1, points[i]);
if (findSide(p1, p2, points[i]) == side && currDist > maxDist) {
ind = i;
maxDist = currDist;
}
}
std::vector<Point> convexHull;
return convexHull;
}
int main() {
int n;
std::cout << "Enter the number of points: ";
std::cin >> n;
std::vector<Point> points(n);
std::cout << "Enter the points (x y):\n";
for (int i = 0; i < n; i++) {
std::cin >> points[i].x >> points[i].y;
}
return 0;
}
Sample Input:
5
11
25
43
51
72
Sample Output:
Convex Hull points:
(1, 1)
(5, 1)
(7, 2)
(2, 5)
Remarks:
Quick Hull is an efficient algorithm for finding the convex hull of a set of points. It's commonly used in computer graphics and
computational geometry. The algorithm divides the problem into smaller subproblems and recursively constructs the convex
hull by finding extreme points. It's more efficient than brute force approaches, especially for large datasets.
15.Problem Name: Write a C++ program and algorithm for Bucket Sort
Analysis: Bucket sort is a sorting technique that involves dividing elements into various groups, or buckets. These buckets are
formed by uniformly distributing the elements. Once the elements are divided into buckets, they can be sorted using any other
sorting algorithm. Finally, the sorted elements are gathered together in an ordered fashion..
Algorithm:
BUCKET_SORT(A)
// A is an array of size n
Create n buckets represented by B
for i ← 1 to n do
Insert A[i] into bucket B[n*A[i]]
end
for i ← 1 to n do
sort each bucket i using insertion sort
end
Concate all buckets into sorted order
Source Code:
#include <iostream>
#include <vector>
#include <algorithm>
void bucketSort(std::vector<float>& arr) {
int n = arr.size();
std::vector<std::vector<float>> buckets(n);
for (float num : arr) {
int index = n * num;
buckets[index].push_back(num);}
for (std::vector<float>& bucket : buckets) {
std::sort(bucket.begin(), bucket.end());}
int index = 0;
for (const std::vector<float>& bucket : buckets) {
for (float num : bucket) {
arr[index++] = num; } }}
int main() {
int n;
std::cout << "Enter the number of elements: ";
std::cin >> n;
std::vector<float> arr(n);
std::cout << "Enter the elements (float values between 0 and 1):\n";
for (int i = 0; i < n; i++) {
std::cin >> arr[i];}
std::cout << "Original array: ";
for (float num : arr) {
std::cout << num << " ";}
std::cout << std::endl;
bucketSort(arr);
std::cout << "Sorted array: ";
for (float num : arr) {
std::cout << num << " "; }
std::cout << std::endl;
return 0;
}
Sample Input: 5
34 3.2 56 90 1
Sample Output:
Original array: 34 3.2 56 90 1
Sorted Array: 1 3.2 34 56 90
Remarks:Bucket Sort has an average time complexity of O(n + n^2/k + k), where n is the number of elements and k is the
number of buckets. It can be efficient for distributing data uniformly within a specified range. Bucket sort can also be used as an
external sorting algorithm. Bucket sort can be stable, and it depends on the algorithm used to sort those elements. Bucket sort
uses spacing to sort the elements.