Default-image

Sorting algorithms help to improve the organization of data, allowing you to find information faster during searching and improve both memory usage and efficiency in data processing. I would say that close to 90% of software development lies in the realm of data processing and data analytics and 10% is related to hardware drivers, robotics and control systems. So I felt while the focus of the course was on Arduino programming it was important to also cover the broader aspects of programming in general. There are several mainstream sorting algorithms which you can do a google search for an investigate them, but I plan to cover a few of the easier to describe ones here.

If you want to write code for these algorithms, while it is possible on the Arduino IDE it requires using the serial commands to be effective and it is far better to write this on a computer using a standard C++ compiler. You have a couple of different options available depending on if you want to be able run the stuff you write while offline, or just want to see the code work.

If you want to just see the code work, the easiest way to do this is to utilize one of the many free online compilers my two favorite ones are:

As for keeping your code locally and running it on your own computer, while it takes a little more work, is the best option. There are a couple different options

  • Microsoft Visual Studio if you are running Windows: You can find it here https://visualstudio.microsoft.com/vs/features/cplusplus/.
  • Use X-Code if you are running MacOS you can find it here: https://developer.apple.com/xcode/
  • Use a test editor and g++ compilers if you are running Linux. On Ubuntu linux you can install them with “apt install g++” and I personally like vim or emac for editing text files “apt install vim” or “apt install emacs”

Now that we have the tools out of the way lets get down to the Algorithms

Insertion Sort

I like to describe this one by how most people sort cards in the hands when playing card games, you pick up the first card and put it in your hand, you pick up the second card and place it to the left if it is lower or to the right if it is higher. You pick up the third card and place to the left if it is lower than the first card, in the middle if it is higher than the first card, or lower than the second card or in-between if it is higher than the second card. This process continues until you have placed all the cards either at the end or in between the other cards based on their values.

#include <iostream>
#include <vector>
using namespace std;

void printarry(vector<int>& arr) {
    int n = arr.size();
    for (int i = 0; i <n; ++i)
        cout << arr[i] << " ";
    cout << "\n";
}

void insertionsort(vector<int>& arr, int n) {
    for (int i = 1; i < n; ++i) {
        printarry(arr);
        int key = arr[i];
        int j = i - 1;

        // move elements greater than key one position ahead
        while (j >= 0 && arr[j] > key) {
            arr[j + 1] = arr[j];
            j--;
        }
        arr[j + 1] = key;
    }
}

int main() {
    vector<int> arr = {12, 11, 13, 5, 6};
    int n = arr.size();
    insertionsort(arr, n);
    printarry(arr);
    return 0;
}

Bubble Sort

This one is fairly easy to describe with cards as well, only in this case you look at two consecutive cards and if the first card is higher than the second you swap them, you repeat this process through the stack of cards an equal number of times as the number of cards and at the end all the cards will be sorted. You can add an extra flag that indicated if any cards were swapped on the last pass and can stop the sort early if no swaps were made which can speed up the algorithm.

#include <iostream>
#include <vector>
using namespace std;

// A function to print an array
void printarray(vector<int>& arr) {
    int n = arr.size();
    for (int i = 0; i <n; ++i)
        cout << arr[i] << " ";
    cout << "\n";
}

// An optimized version of Bubble Sort 
void bubbleSort(vector<int>& arr) {
    int n = arr.size();
    bool swapped;
  
    for (int i = 0; i < n - 1; i++) {
        swapped = false;
        for (int j = 0; j < n - i - 1; j++) {
            if (arr[j] > arr[j + 1]) {
                swap(arr[j], arr[j + 1]);
                swapped = true;
            }
        }
        printarray(arr);
        // If no two elements were swapped, then break
        if (!swapped)
            break;
    }
}

int main() {
    vector<int> arr = { 5, 6, 1, 3 };
    bubbleSort(arr); 
    printarray(arr);
}

Selection Sort

This one can be described like this. You take a stack of cards in your hand and look through the stack to find the lowest card and place it first. You then make a second pass and find the lowest remaining card and place it second. and so on until you have placed all the cards.

#include <iostream>
#include <vector>
using namespace std;

// A function to print an array
void printarray(vector<int>& arr) {
    int n = arr.size();
    for (int i = 0; i <n; ++i)
        cout << arr[i] << " ";
    cout << "\n";
}

void selectionSort(vector<int> &arr) {
    int n = arr.size();

    for (int i = 0; i < n - 1; ++i) {

        // Assume the current position holds
        // the minimum element
        int min_idx = i;

        // Iterate through the unsorted portion
        // to find the actual minimum
        for (int j = i + 1; j < n; ++j) {
            if (arr[j] < arr[min_idx]) {

                // Update min_idx if a smaller
                // element is found
                min_idx = j; 
            }
        }

        // Move minimum element to its
        // correct position
        swap(arr[i], arr[min_idx]);
        printarray(arr);
    }
}

int main() {
    vector<int> arr = {64, 25, 12, 22, 11};
    selectionSort(arr);
    printarray(arr);
    return 0;
}

Counting Sort

The counting sort is not a comparison based sorting algorithm it is more what I would call a stacking sort algorithm. In the counting sort you look through the stack of cards and find the highest and lowest value. Then you create a separate array to store the count of each value. Next you start with the lowest value in the sequence and count the number of cards that match that value and move to the next value, the value can have zero cards in this case. Then you read the count array and write the numbers back into the original array sorted.

So you can think of them like stacking the cards of the same value in piles that are in number order. then picking up all the cards.

#include <iostream>
#include <vector>
using namespace std;

// A function to print an array
void printarray(vector<int>& arr) {
    int n = arr.size();
    for (int i = 0; i <n; ++i)
        cout << arr[i] << " ";
    cout << "\n";
}

vector<int> countsort(vector<int>& arr) {
    int n = arr.size();
    printarray(arr);
    // find the maximum element
    int maxval = 0;
    for (int i = 0; i < n; i++)
        maxval = max(maxval, arr[i]);

    // create and initialize count array
    vector<int> count(maxval + 1, 0);

    // count frequency of each element
    for (int i = 0; i < n; i++)
        count[arr[i]]++;

    // compute prefix sum
    for (int i = 1; i <= maxval; i++)
        count[i] += count[i - 1];
    printarray(count);
    // build output array
    vector<int> ans(n);
    for (int i = n - 1; i >= 0; i--) {
        ans[count[arr[i]] - 1] = arr[i];
        count[arr[i]]--;
    }

    return ans;
}

int main() {
    vector<int> arr = {2,5,3,0,2,3,0,3};
    vector<int> sorted = countsort(arr);

    printarray(sorted);

    return 0;
}

Quick Sort

In the quick sort you chose a pivot point or “card” and then we arrange the list around the pivot recursively. There are different methods to picking the pivot, from always choosing the first element, always choosing the last element, always choosing a random element, or finding the median and using it. The last option while it will always split the array in half each time it actually slows down the sort. The best seems to be picking a random card and the worst is picking the last card if the cards happen to already be sorted. So here is the basic concept. You pick a random card from the pile of cards and split the cards into two piles, cards lower than the pivot and cards higher than the pivot. You repeat this process with each stack of cards until only one card remains in each stack. As you will have stacked the cards below and above the original pivot you can now just pick the single card stacks up in order. This is considered a partitioning algorithm because it splits or partitions the data being sorted until it can’t be partitioned any smaller. The quick sort is still the fastest sort algorithm for computers today.

The key process in quickSort is a partition(). There are three common algorithms to partition. All these algorithms have O(n) time complexity.

  1. Naive Partition: Here we create copy of the array. First put all smaller elements and then all greater. Finally we copy the temporary array back to original array. This requires O(n) extra space.
  2. Lomuto Partition: We have used this partition in this article. This is a simple algorithm, we keep track of index of smaller elements and keep swapping. We have used it here in this article because of its simplicity.
  3. Hoare’s Partition: This is the fastest of all. Here we traverse array from both sides and keep swapping greater element on left with smaller on right while the array is not partitioned. Please refer Hoare’s vs Lomuto for details.

The key process in quickSort is a partition(). There are three common algorithms to partition.

Naive Partition: Here we create copy of the array. First put all smaller elements and then all greater. Finally we copy the temporary array back to original array. This requires O(n) extra space.
Lomuto Partition: I used this partition in this article. This is a simple algorithm, we keep track of index of smaller elements and keep swapping. I used it here in this article because of its simplicity.
Hoare’s Partition: This is the fastest of all. Here we traverse array from both sides and keep swapping greater element on left with smaller on right while the array is not partitioned. Please refer Hoare’s vs Lomuto for details.

#include <iostream>
#include <vector>
using namespace std;

// A function to print an array
void printarray(vector<int>& arr) {
    int n = arr.size();
    for (int i = 0; i <n; ++i)
        cout << arr[i] << " ";
    cout << "\n";
}

int partition(vector<int>& arr, int low, int high) {
  
    // choose the pivot
    int pivot = arr[high];
  
    // undex of smaller element and indicates 
    // the right position of pivot found so far
    int i = low - 1;

    // Traverse arr[low..high] and move all smaller
    // elements on left side. Elements from low to 
    // i are smaller after every iteration
    for (int j = low; j <= high - 1; j++) {
        if (arr[j] < pivot) {
            i++;
            swap(arr[i], arr[j]);
        }
    }
    
    // move pivot after smaller elements and
    // return its position
    swap(arr[i + 1], arr[high]);  
    return i + 1;
}

// the QuickSort function implementation
void quickSort(vector<int>& arr, int low, int high) {
    cout << "low:" << low << " high:" << high <<" arr:";
    printarray(arr);
    if (low < high) {
      
        // pi is the partition return index of pivot
        int pi = partition(arr, low, high);

        // recursion calls for smaller elements
        // and greater or equals elements
        quickSort(arr, low, pi - 1);
        quickSort(arr, pi + 1, high);
    }
}

int main() {
    vector<int> arr = {10, 7, 8, 9, 1, 5};
    int n = arr.size();
    quickSort(arr, 0, n - 1);
  
    printarray(arr);
    return 0;
}

Leave a Reply

Share via
Copy link
Powered by Social Snap