How to resolve the algorithm Sorting algorithms/Quicksort step by step in the C# programming language

Published on 12 May 2024 09:40 PM

How to resolve the algorithm Sorting algorithms/Quicksort step by step in the C# programming language

Table of Contents

Problem Statement

Sort an array (or list) elements using the   quicksort   algorithm. The elements must have a   strict weak order   and the index of the array can be of any discrete type. For languages where this is not possible, sort an array of integers.

Quicksort, also known as   partition-exchange sort,   uses these steps.

The best pivot creates partitions of equal length (or lengths differing by   1). The worst pivot creates an empty partition (for example, if the pivot is the first or last element of a sorted array). The run-time of Quicksort ranges from   O(n log n)   with the best pivots, to   O(n2)   with the worst pivots, where   n   is the number of elements in the array.

This is a simple quicksort algorithm, adapted from Wikipedia. A better quicksort algorithm works in place, by swapping elements within the array, to avoid the memory allocation of more arrays. Quicksort has a reputation as the fastest sort. Optimized variants of quicksort are common features of many languages and libraries. One often contrasts quicksort with   merge sort,   because both sorts have an average time of   O(n log n). Quicksort is at one end of the spectrum of divide-and-conquer algorithms, with merge sort at the opposite end.

With quicksort, every element in the first partition is less than or equal to every element in the second partition. Therefore, the merge phase of quicksort is so trivial that it needs no mention! This task has not specified whether to allocate new arrays, or sort in place. This task also has not specified how to choose the pivot element. (Common ways to are to choose the first element, the middle element, or the median of three elements.) Thus there is a variety among the following implementations.

Let's start with the solution:

Step by Step solution about How to resolve the algorithm Sorting algorithms/Quicksort step by step in the C# programming language

Quick Sort Algorithm

The provided C# code implements a modified version of the Quick Sort algorithm, which is a divide-and-conquer sorting algorithm. It is known for its efficiency, particularly for large datasets. Here's a detailed explanation of the code:

QuickSort Class

The QuickSort<T> class encapsulates the Quick Sort algorithm for elements of type T that implement the IComparable interface.

  • Constants:

    • INSERTION_LIMIT_DEFAULT: A constant defining the default maximum size for insertion sort, which is used for small arrays.
    • SAMPLES_MAX: A constant defining the maximum number of samples taken for pivot estimation.
  • Properties:

    • InsertionLimit: The insertion limit used for small arrays.
    • Samples: An array used to store samples for pivot estimation.
    • Left: The left index of the current range being sorted.
    • Right: The right index of the current range being sorted.
    • LeftMedian and RightMedian: Indices used to track median elements during tripartite partitioning (optional feature).
  • Sort Methods:

    • Sort(T[] entries): Sorts the entire array of type T.
    • Sort(T[] entries, Int32 first, Int32 last): Sorts a range of elements within the array.
  • Pivot Estimation:

    • pivot(T[] entries): Estimates the median value within the current range by sampling a proportional number of elements. It uses the median of these samples as the pivot.
  • Partitioning:

    • partition(T median, T[] entries): Partitions the array using the pivot value. It places elements less than the pivot to the left of the pivot, elements greater than the pivot to the right of the pivot, and elements equal to the pivot at their original positions (when Tripartite is defined).

Insertion Sort (Nested Class)

The nested InsertionSort<T> class contains a static method for insertion sorting small arrays.

Swap Methods:

  • Swap(ref T e1, ref T e2): Swaps two elements of type T.
  • Swap(T[] entries, Int32 left, Int32 right): Swaps two elements in the array.

Tripartite Partitioning (Optional Feature)

When Tripartite is defined, the partitioning method isolates islands of keys that are equal to the pivot value. This is useful when key-equivalent classes are large, but it incurs additional comparison overhead.

Usage

The provided code includes usage examples in two different classes:

  • Sort Example in Main Function:

    • Creates an array of integers {1, 3, 5, 7, 9, 8, 6, 4, 2}.
    • Sorts the array using the QuickSort class.
    • Outputs the sorted array.
  • QSort Example:

    • Defines a QSorter class with a static QSort method.
    • The QSort method takes an IEnumerable<IComparable> and uses a recursive approach to sort it.
    • This example demonstrates the use of Quick Sort on an iterable collection of any type that implements IComparable.

Source code in the csharp programming language

//
// The Tripartite conditional enables Bentley-McIlroy 3-way Partitioning.
// This performs additional compares to isolate islands of keys equal to
// the pivot value.  Use unless key-equivalent classes are of small size.
//
#define Tripartite

namespace RosettaCode {
  using System;
  using System.Diagnostics;

  public class QuickSort<T> where T : IComparable {
    #region Constants
    public const UInt32 INSERTION_LIMIT_DEFAULT = 12;
    private const Int32 SAMPLES_MAX = 19;
    #endregion

    #region Properties
    public UInt32 InsertionLimit { get; }
    private T[] Samples { get; }
    private Int32 Left { get; set; }
    private Int32 Right { get; set; }
    private Int32 LeftMedian { get; set; }
    private Int32 RightMedian { get; set; }
    #endregion

    #region Constructors
    public QuickSort(UInt32 insertionLimit = INSERTION_LIMIT_DEFAULT) {
      this.InsertionLimit = insertionLimit;
      this.Samples = new T[SAMPLES_MAX];
    }
    #endregion

    #region Sort Methods
    public void Sort(T[] entries) {
      Sort(entries, 0, entries.Length - 1);
    }

    public void Sort(T[] entries, Int32 first, Int32 last) {
      var length = last + 1 - first;
      while (length > 1) {
        if (length < InsertionLimit) {
          InsertionSort<T>.Sort(entries, first, last);
          return;
        }

        Left = first;
        Right = last;
        var median = pivot(entries);
        partition(median, entries);
        //[Note]Right < Left

        var leftLength = Right + 1 - first;
        var rightLength = last + 1 - Left;

        //
        // First recurse over shorter partition, then loop
        // on the longer partition to elide tail recursion.
        //
        if (leftLength < rightLength) {
          Sort(entries, first, Right);
          first = Left;
          length = rightLength;
        }
        else {
          Sort(entries, Left, last);
          last = Right;
          length = leftLength;
        }
      }
    }

    /// <summary>Return an odd sample size proportional to the log of a large interval size.</summary>
    private static Int32 sampleSize(Int32 length, Int32 max = SAMPLES_MAX) {
      var logLen = (Int32)Math.Log10(length);
      var samples = Math.Min(2 * logLen + 1, max);
      return Math.Min(samples, length);
    }

    /// <summary>Estimate the median value of entries[Left:Right]</summary>
    /// <remarks>A sample median is used as an estimate the true median.</remarks>
    private T pivot(T[] entries) {
      var length = Right + 1 - Left;
      var samples = sampleSize(length);
      // Sample Linearly:
      for (var sample = 0; sample < samples; sample++) {
        // Guard against Arithmetic Overflow:
        var index = (Int64)length * sample / samples + Left;
        Samples[sample] = entries[index];
      }

      InsertionSort<T>.Sort(Samples, 0, samples - 1);
      return Samples[samples / 2];
    }

    private void partition(T median, T[] entries) {
      var first = Left;
      var last = Right;
#if Tripartite
      LeftMedian = first;
      RightMedian = last;
#endif
      while (true) {
        //[Assert]There exists some index >= Left where entries[index] >= median
        //[Assert]There exists some index <= Right where entries[index] <= median
        // So, there is no need for Left or Right bound checks
        while (median.CompareTo(entries[Left]) > 0) Left++;
        while (median.CompareTo(entries[Right]) < 0) Right--;

        //[Assert]entries[Right] <= median <= entries[Left]
        if (Right <= Left) break;

        Swap(entries, Left, Right);
        swapOut(median, entries);
        Left++;
        Right--;
        //[Assert]entries[first:Left - 1] <= median <= entries[Right + 1:last]
      }

      if (Left == Right) {
        Left++;
        Right--;
      }
      //[Assert]Right < Left
      swapIn(entries, first, last);

      //[Assert]entries[first:Right] <= median <= entries[Left:last]
      //[Assert]entries[Right + 1:Left - 1] == median when non-empty
    }
    #endregion

    #region Swap Methods
    [Conditional("Tripartite")]
    private void swapOut(T median, T[] entries) {
      if (median.CompareTo(entries[Left]) == 0) Swap(entries, LeftMedian++, Left);
      if (median.CompareTo(entries[Right]) == 0) Swap(entries, Right, RightMedian--);
    }

    [Conditional("Tripartite")]
    private void swapIn(T[] entries, Int32 first, Int32 last) {
      // Restore Median entries
      while (first < LeftMedian) Swap(entries, first++, Right--);
      while (RightMedian < last) Swap(entries, Left++, last--);
    }

    /// <summary>Swap entries at the left and right indicies.</summary>
    public void Swap(T[] entries, Int32 left, Int32 right) {
      Swap(ref entries[left], ref entries[right]);
    }

    /// <summary>Swap two entities of type T.</summary>
    public static void Swap(ref T e1, ref T e2) {
      var e = e1;
      e1 = e2;
      e2 = e;
    }
    #endregion
  }

  #region Insertion Sort
  static class InsertionSort<T> where T : IComparable {
    public static void Sort(T[] entries, Int32 first, Int32 last) {
      for (var next = first + 1; next <= last; next++)
        insert(entries, first, next);
    }

    /// <summary>Bubble next entry up to its sorted location, assuming entries[first:next - 1] are already sorted.</summary>
    private static void insert(T[] entries, Int32 first, Int32 next) {
      var entry = entries[next];
      while (next > first && entries[next - 1].CompareTo(entry) > 0)
        entries[next] = entries[--next];
      entries[next] = entry;
    }
  }
  #endregion
}


  using Sort;
  using System;

  class Program {
    static void Main(String[] args) {
      var entries = new Int32[] { 1, 3, 5, 7, 9, 8, 6, 4, 2 };
      var sorter = new QuickSort<Int32>();
      sorter.Sort(entries);
      Console.WriteLine(String.Join(" ", entries));
    }
  }


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

namespace QSort
{
    class QSorter
    {
        private static IEnumerable<IComparable> empty = new List<IComparable>();

        public static IEnumerable<IComparable> QSort(IEnumerable<IComparable> iEnumerable)
        {
            if(iEnumerable.Any())
            {
                var pivot = iEnumerable.First();
                return QSort(iEnumerable.Where((anItem) => pivot.CompareTo(anItem) > 0)).
                    Concat(iEnumerable.Where((anItem) => pivot.CompareTo(anItem) == 0)).
                    Concat(QSort(iEnumerable.Where((anItem) => pivot.CompareTo(anItem) < 0)));
            }
            return empty;
        }
    }
}


  

You may also check:How to resolve the algorithm Terminal control/Ringing the terminal bell step by step in the Forth programming language
You may also check:How to resolve the algorithm Return multiple values step by step in the ALGOL 68 programming language
You may also check:How to resolve the algorithm Take notes on the command line step by step in the 8086 Assembly programming language
You may also check:How to resolve the algorithm Guess the number/With feedback step by step in the AutoHotkey programming language
You may also check:How to resolve the algorithm Pinstripe/Display step by step in the Scala programming language