Day 11: Sorting Basics

Day 11: Sorting Basics

Sorting is one of the foundational concepts in computer science, used to organize data for better accessibility and efficiency in solving problems. On Day 11 of my 100 Days of DSA challenge, I’ll be focusing on implementing sorting algorithms, understanding their time and space complexities, and solving problems that rely on efficient data arrangement.

Check out my GitHub repository for all the solutions and progress updates at: 100 Days of DSA

Let’s dive into today’s challenges and learn how sorting can optimize solutions!


1. Bubble Sort

This program repeatedly compares adjacent elements and swaps them if they are out of order, moving the largest unsorted element to its correct position in each pass. The process continues until the array is fully sorted or no swaps are needed in a pass, indicating the array is already sorted. Bubble Sort is an in-place sorting algorithm, meaning it does not require additional space proportional to the input size. However, it is inefficient on large datasets due to its O(n²) time complexity.

Code:

#include <iostream>
using namespace std;

// Function to perform Bubble Sort
void bubble_sort(int arr[], int n) {
    bool swapped;

    for (int i = 0; i < n - 1; ++i) {
        swapped = false; // Track if a swap was made in this pass
        for (int j = 0; j < n - i - 1; ++j) {
            if (arr[j] > arr[j + 1]) {
                int temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
                swapped = true;
            }
        }
        // If no two elements were swapped, the array is already sorted
        if (!swapped) break;
    }
}

void print_array(int arr[], int n) {
    for (int i = 0; i < n; ++i) {
        cout << arr[i] << " ";
    }
    cout << endl;
}

int main() {
    int arr[] = {1, 4, 2, 6, 3, 5};
    int n = sizeof(arr) / sizeof(arr[0]);
    cout << "Original array: ";
    print_array(arr, n);
    bubble_sort(arr, n);
    cout << "Sorted array: ";
    print_array(arr, n);
    return 0;
}

Output:


2. Selection Sort

This program implements Selection Sort, where the algorithm repeatedly identifies the smallest element from the unsorted portion of the array and swaps it with the first element of that portion. This process continues, shifting the boundary of the sorted section forward, until the entire array is sorted. Selection Sort is an in-place algorithm and requires no additional space. While it’s more intuitive than Bubble Sort, it still has a time complexity of O(n²).

Code:

#include <iostream>
using namespace std;

// Function to perform Selection Sort
void selection_sort(int arr[], int n) {
    for (int i = 0; i < n - 1; i++) {
        int minIndex = i;             // Assume the first element is the smallest
        for (int j = i + 1; j < n; j++) {
            if (arr[j] < arr[minIndex]) {
                minIndex = j;         // Update the index of the smallest element
            }
        }
        // Swap the found minimum element with the first element of the unsorted part
        int temp = arr[i];
        arr[i] = arr[minIndex];
        arr[minIndex] = temp;
    }
}

void print_array(int arr[], int n) {
    for (int i = 0; i < n; ++i) {
        cout << arr[i] << " ";
    }
    cout << endl;
}

int main() {
    int arr[] = {10, 50, 20, 30, 40};
    int n = sizeof(arr) / sizeof(arr[0]); 
    cout << "Original array: ";
    print_array(arr, n);
    selection_sort(arr, n);
    cout << "Sorted array: ";
    print_array(arr, n);
    return 0;
}

Output:


3. Sort an Array of 0s, 1s, and 2s

The code sorts an array of 0s, 1s, and 2s using the Dutch National Flag algorithm. Three pointers (low, mid, and high) divide the array into regions for 0s, 1s, and 2s. The algorithm processes each element, swapping 0s to the start, 2s to the end, and leaving 1s in place, ensuring the array is sorted in a single traversal.

Code:

#include <iostream>
using namespace std;

// Function to sort an array of 0s, 1s, and 2s
void sort_array(int arr[], int n) {
    int low = 0, mid = 0, high = n - 1;

    while (mid <= high) {
        if (arr[mid] == 0) {
            // Swap arr[low] and arr[mid], increment low and mid
            swap(arr[low], arr[mid]);
            low++;
            mid++;
        } else if (arr[mid] == 1) {
            // Move mid pointer
            mid++;
        } else { 
            // arr[mid] == 2
            // Swap arr[mid] and arr[high], decrement high
            swap(arr[mid], arr[high]);
            high--;
        }
    }
}

void print_array(int arr[], int n) {
    for (int i = 0; i < n; ++i) {
        cout << arr[i] << " ";
    }
    cout << endl;
}

int main() {
    int arr[] = {0, 1, 2, 1, 0, 2, 1, 0, 2};
    int n = sizeof(arr) / sizeof(arr[0]); 
    cout << "Original array: ";
    print_array(arr, n);
    sort_array(arr, n);
    cout << "Sorted array: ";
    print_array(arr, n);
    return 0;
}

Output:


4. Insertion Sort

This program implements Insertion Sort, which iteratively places each element in its correct position within the sorted portion of the array by shifting larger elements to the right. It also counts the number of shifts made during the sorting process, including shifts for repositioning and final placements of elements. It’s space complexity is O(1) as sorting is performed in-place. The algorithm has a worst-case time complexity of O(n²), but it’s efficient for nearly sorted data.

Code:

#include <iostream>
using namespace std;

// Function to perform Insertion Sort and count shifts
int insertion_sort(int arr[], int n) {
    int count = 0; 

    for (int i = 1; i < n; ++i) {
        int key = arr[i];
        int j = i - 1;

        // Shift elements of arr[0..i-1] that are greater than key
        while (j >= 0 && arr[j] > key) {
            arr[j + 1] = arr[j];
            j--;
            count++; 
        }

        // Place the key in its correct position
        arr[j + 1] = key;
        count++; // Increment for the final placement of key
    }

    return count;
}

void print_array(int arr[], int n) {
    for (int i = 0; i < n; ++i) {
        cout << arr[i] << " ";
    }
    cout << endl;
}

int main() {
    int arr[] = {5, 3, 4, 2, 1};
    int n = sizeof(arr) / sizeof(arr[0]);
    cout << "Original array: ";
    print_array(arr, n);
    int shiftCount = insertion_sort(arr, n);
    cout << "Sorted array: ";
    print_array(arr, n);
    cout << "Number of shifts: " << shiftCount << endl;
    return 0;
}

Output:


5. Merge Two Sorted Arrays Without Extra Space

This program merges two sorted arrays by comparing the largest element in the first array with the smallest element in the second array, swapping them if necessary. After the initial swaps, both arrays are sorted individually using std::sort(). The result is two sorted arrays, which are printed separately. The approach modifies the arrays in place without using extra space for merging.

Code:

#include <iostream>
#include <algorithm>

using namespace std;

// Function to merge two sorted arrays into one sorted array in-place
void merge_arrays(int arr1[], int arr2[], int n, int m) {
    int i = n - 1;  // Pointer for the last element of arr1
    int j = 0;      // Pointer for the first element of arr2

    // Traverse both arrays
    while (i >= 0 && j < m) {
        // If arr1[i] is greater than arr2[j], swap them
        if (arr1[i] > arr2[j]) {
            // Swap arr1[i] and arr2[j]
            swap(arr1[i], arr2[j]);

            // Move the pointers
            i--;
            j++;
        } else {
            break;  // No need to swap if arr1[i] <= arr2[j]
        }
    }

    // After swapping, arr1 and arr2 might still not be sorted, so sort both arrays
    sort(arr1, arr1 + n);  // Sort arr1
    sort(arr2, arr2 + m);  // Sort arr2
}

void print_array(int arr[], int size) {
    for (int i = 0; i < size; ++i) {
        cout << arr[i] << " ";
    };
}

int main() {
    int arr1[] = {1, 5, 9, 10, 15};
    int arr2[] = {2, 3, 8, 13};
    int n = sizeof(arr1) / sizeof(arr1[0]); 
    int m = sizeof(arr2) / sizeof(arr2[0]); 
    cout << "Original arrays: " << endl;
    cout << "Array 1: ";
    print_array(arr1, n);
    cout << endl;
    cout << "Array 2: ";
    print_array(arr2, m);
    cout << endl;
    merge_arrays(arr1, arr2, n, m);
    cout << "Merged and sorted array: ";
    print_array(arr1, n);
    print_array(arr2, m);

    return 0;
}

Output:


Day 11 was all about mastering essential sorting techniques and exploring their real-world applications. Through problems like sorting arrays of specific elements and efficiently merging arrays, I gained valuable insights into optimizing sorting processes. These challenges have laid a solid foundation for diving deeper into more complex sorting algorithms in the days ahead. Excited to continue this journey of learning and improving! 🚀