Common Sorting Algorithms

Posted on

Problem

Here I’ve prepared my solutions for the following algorithms, and was curious if there were a way to optimize any of them. Any input is welcome and appreciated!

using System;
using System.Collections.Generic;
using System.Linq;

namespace algotest
{
    class Program
    {
        static void InsertionSort(int[] arr)
        {
            for (int i = 1; i < arr.Length; ++i)
            {
                int temp = arr[i];
                bool sorted = false;

                for (int j = i - 1; j >= 0 && !sorted;)
                {
                    if (temp < arr[j])
                    {
                        arr[j + 1] = arr[j];
                        --j;
                        arr[j + 1] = temp;
                    }
                    else
                    {
                        sorted = true;
                    }
                }
            }
        }

        static void BubbleSort(int[] arr)
        {
            int temp = 0;

            for (int i = 0; i < arr.Length; ++i)
            {
                for (int j = 0; j < arr.Length - 1; ++j)
                {
                    if (arr[j] > arr[j + 1])
                    {
                        temp = arr[j + 1];
                        arr[j + 1] = arr[j];
                        arr[j] = temp;
                    }
                }
            }
        }

        static void SelectionSort(int[] arr)
        {
            int temp, min;

            for (int i = 0; i < arr.Length - 1; ++i)
            {
                min = i;

                for (int j = i + 1; j < arr.Length; ++j)
                {
                    if (arr[j] < arr[min])
                    {
                        min = j;
                    }
                }

                temp = arr[min];
                arr[min] = arr[i];
                arr[i] = temp;
            }
        }

        static void Merge(int[] arr, int left, int pivot, int right)
        {
            int[] temp = new int[25];
            int index = left;
            int leftBound = pivot - 1;
            int length = right - left + 1;

            while (left <= leftBound && pivot <= right)
            {
                if (arr[left] <= arr[pivot])
                {
                    temp[index++] = arr[left++];
                }
                else
                {
                    temp[index++] = arr[pivot++];
                }
            }

            while (left <= leftBound)
            {
                temp[index++] = arr[left++];
            }

            while (pivot <= right)
            {
                temp[index++] = arr[pivot++];
            }

            for (int i = 0; i < length; i++)
            {
                arr[right] = temp[right];
                right--;
            }
        }

        static void MergeSort(int[] arr, int left, int right)
        {
            if (right > left)
            {
                int pivot = (right + left) / 2;
                MergeSort(arr, left, pivot);
                MergeSort(arr, pivot + 1, right);
                Merge(arr, left, pivot + 1, right);
            }
        }

        static void QuickSort(int[] arr, int left, int right)
        {
            int i = left, j = right;
            int pivot = arr[(left + right) / 2];

            while (i <= j)
            {
                while (arr[i].CompareTo(pivot) < 0)
                {
                    ++i;
                }

                while (arr[j].CompareTo(pivot) > 0)
                {
                    --j;
                }

                if (i <= j)
                {
                    int temp = arr[i];
                    arr[i] = arr[j];
                    arr[j] = temp;

                    ++i;
                    --j;
                }
            }

            if (left < j)
            {
                QuickSort(arr, left, j);
            }

            if (i < right)
            {
                QuickSort(arr, i, right);
            }
        }

        static void Heapify(int[] arr, int i, int n)
        {
            try
            {
                int temp = arr[i];
                int j = 2 * i;

                while (j <= n)
                {
                    if (j < n && arr[j] < arr[j + 1])
                    {
                        j++;
                    }

                    if (temp >= arr[j])
                    {
                        break;
                    }

                    arr[j / 2] = arr[j];
                    j *= 2;
                }

                arr[j / 2] = temp;
            }
            catch (IndexOutOfRangeException err)
            {
                Console.WriteLine("Array Out of Bounds ", err);
            }
        }

        static void HeapSort(int[] arr)
        {
            for (int i = arr.Length / 2; i >= 0; --i)
            {
                Heapify(arr, i, arr.Length - 1);
            }

            for (int i = arr.Length - 2; i >= 0; --i)
            {
                int temp = arr[i + 1];
                arr[i + 1] = arr[0];
                arr[0] = temp;

                Heapify(arr, 0, i);
            }
        }

        static void CountingSort(int[] arr)
        {
            int[] count = new int[arr.Max() + 1];

            for (int i = 0; i < arr.Length; ++i)
            {
                ++count[arr[i]];
            }

            int j = 0;

            for (int i = 0; i < count.Length; ++i)
            {
                for (int k = 0; k < count[i]; ++k)
                {
                    arr[j++] = i;
                }
            }
        }

        static void RadixSort(int[] arr)
        {
            int i, j;
            int[] temp = new int[arr.Length];

            for (int shift = 31; shift > -1; --shift)
            {
                j = 0;

                for (i = 0; i < arr.Length; ++i)
                {
                    bool move = (arr[i] << shift) >= 0;

                    if (shift == 0 ? !move : move)
                    {
                        arr[i - j] = arr[i];
                    }
                    else
                    {
                        temp[j++] = arr[i];
                    }
                }

                Array.Copy(temp, 0, arr, arr.Length - j, j);
            }
        }

        static void BucketSort(int[] arr)
        {
            // bucket size key:
            // array 1-99 ; 10 buckets
            // arrat 100-199 ; 20 buckets
            // etc.
            // TODO: Implement logic to determine bucket size?

            int numOfBuckets = 10;
            List<int> result = new List<int>();
            List<int>[] buckets = new List<int>[numOfBuckets];

            for (int i = 0; i < numOfBuckets; ++i)
            {
                buckets[i] = new List<int>();
            }

            for (int i = 0; i < arr.Length; ++i)
            {
                int index = arr[i] / numOfBuckets;
                buckets[index].Add(arr[i]);
            }

            for (int i = 0; i < numOfBuckets; ++i)
            {
                int[] temp = buckets[i].ToArray();
                InsertionSort(arr);
                result.AddRange(temp);
            }

            arr = result.ToArray();
        }

        static void Main(string[] args)
        {
            Console.WriteLine("Please select an algorithm: ");
            Console.WriteLine("I : InsertionSort O(n^2)");
            Console.WriteLine("B : BubbleSort O(n^2)");
            Console.WriteLine("S : SelectionSort O(n^2)");
            Console.WriteLine("M : MergeSort O(nlgn)");
            Console.WriteLine("Q : QuickSort O(nlgn)");
            Console.WriteLine("H : HeapSort O(nlgn)");
            Console.WriteLine("C : CountingSort O(n)");
            Console.WriteLine("R : RadixSort O(n)");
            Console.WriteLine("U : BucketSort O(n)");

            string algorithm = Console.ReadLine().ToUpper();

            Console.WriteLine("How many elements?");

            int numOfElements = int.Parse(Console.ReadLine());
            int[] arr = new int[numOfElements];
            Random rand = new Random();

            Console.WriteLine("Generating random data...");

            for (int i = 0; i < numOfElements; ++i)
            {
                arr[i] = rand.Next(100);
            }

            switch (algorithm)
            {
                case "I":
                    InsertionSort(arr);
                    break;
                case "B":
                    BubbleSort(arr);
                    break;
                case "S":
                    SelectionSort(arr);
                    break;
                case "M":
                    MergeSort(arr, 0, arr.Length - 1);
                    break;
                case "Q":
                    QuickSort(arr, 0, arr.Length - 1);
                    break;
                case "H":
                    HeapSort(arr);
                    break;
                case "C":
                    CountingSort(arr);
                    break;
                case "R":
                    RadixSort(arr);
                    break;
                case "U":
                    BucketSort(arr);
                    break;
            }

            for (int i = 0; i < arr.Length; ++i)
            {
                Console.WriteLine(arr[i]);
            }

            Console.WriteLine("Press any key to exit.");
            Console.ReadKey();
        }
    }
}

Solution

Insertion sort

You don’t need the flag variable sorted.
Instead of sorted = true, you can simply break out of the loop.

Many times flag variables can be avoided,
and result in simpler, more readable solutions.
Always look suspiciously at flag variables.

Merge sort

The number 25 in int[] temp = new int[25] looks arbitrary. Why is it 25?

Note that this may lead to integer overflow,
a common pitfall in merge sort implementations:

int pivot = (right + left) / 2;

The fix is simple enough:

int pivot = left + (right - left) / 2;

Counting sort

The implementation allocates an array of size arr.Max() + 1 as storage.
It would be good to also take into account arr.Min(),
to avoid allocating much more space than necessary.

Furthermore,
the current implementation will crash if the input contains negative numbers.
That’s another reason to consider arr.Min() too.

Insertion sort

You appear to be writing the moving element repeatedly, which isn’t needed, and as mentioned there is a sorted flag you don’t need, so you can break out of the loop to place the moving element.

    static void InsertionSort(int[] arr)
    {
        for (int i = 1; i < arr.Length; ++i)
        {
            int temp = arr[i];
            int j = i - 1;

            while (j >= 0)
            {
                if (temp >= arr[j])   break; // found insertion point

                arr[j + 1] = arr[j];
                --j;
            }
            arr[j + 1] = temp;
        }
    }

Bubble sort

Normally bubble sort refers to swapping adjacent elements. It can be optimized by tracking the last swap.

    static void BubbleSort(int[] arr)
    {
        int endPt = arr.Length - 1;
        while (endPt > 0)
        {
            int lastSwap = 0;
            for (int j = 0; j < endPt; ++j)
            {
                if (arr[j] > arr[j+1])
                {
                    int temp = arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] = temp;
                    lastSwap = j;
                }
            }
            endPt = lastSwap;
        }
    }

Selection sort

No issues, although you have variables declared a little further from their point of use than in the previous routines.

… I might look at some more later.

Leave a Reply

Your email address will not be published. Required fields are marked *