C# Introsort

February 27, 2008 – 10:22 am

Here is a C# implementation of the Introsort algorithm. Notice that the Introsort method is nearly identical to the Quicksort method. The only difference is the addition of checking for the depth limit (number of recursions — lines 51-59); when the depth limit has been reached, the Heapsort method is called to complete the sort of the remaining items of the present partition. See David Musser’s paper for an in-depth analysis of the Introsort algorithm, as well as a discussion about choosing the correct recursion depth.

This code is used to sort collections of objects of any class that implement IComparable. It is optimized for readability, not performance (if I were worried about performance I would have used the “unsafe” keyword and sorted everything using pointers), and includes functions for Quicksort, Heapsort, and Introsort, as well as some helper methods (Swap, SiftDown).

   1:  #region Copyright (c) 2008 Michael Ferguson [www.pherg.net]
   2:  /*****************************************************************************
   3:   * File:   Sorter.cs
   4:   * Author: Michael Ferguson (http://www.pherg.net)
   5:   * Date:   19 February 2008
   6:   * 
   7:   * This program is free software: you can redistribute it and/or modify it
   8:   * under the terms of the GNU General Public License as published by the Free
   9:   * Software Foundation, either version 3 of the License, or (at your option)
  10:   * any later version.
  11:   *
  12:   * This program is distributed in the hope that it will be useful, but WITHOUT
  13:   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  14:   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  15:   * more details (http://www.gnu.org/licenses/gpl.txt).
  16:   ****************************************************************************/
  17:  #endregion
  18:   
  19:  using System;
  20:  using System.Collections.Generic;
  21:   
  22:  namespace Pherg
  23:  {
  24:    class Sorter<T> where T:IComparable
  25:    {
  26:      public Sorter ()
  27:      {
  28:      }
  29:   
  30:      public void Introsort (T[] array, int left, int right, int depthLimit)
  31:      {
  32:        if (left >= right)
  33:        {
  34:          // done sorting
  35:          return;
  36:        }
  37:        else if (left == right - 1)
  38:        {
  39:          // two element list
  40:          if (array[left].CompareTo(array[right]) > 0)
  41:          {
  42:            // left is greater than the right — swap them
  43:            Swap(array, left, right);
  44:          }
  45:   
  46:          // done sorting
  47:          return;
  48:        }
  49:        else if (depthLimit == 0)
  50:        {
  51:          // depth limit has reached maximum
  52:          Heapsort(array, left, right);
  53:          return;
  54:        }
  55:   
  56:        // decrement depth limit
  57:        depthLimit -= 1;
  58:   
  59:        // since this method is recursive avoid placing these variables on the
  60:        //   stack until the last moment
  61:        int i = left;
  62:        int k = right;
  63:   
  64:        // choose a pivot and move it to the end of the list
  65:        int pivotIndex = (left + right) >> 1; // (left + right) / 2
  66:        T pivot = array[pivotIndex];
  67:        array[pivotIndex] = array[right];
  68:        array[right] = pivot;
  69:   
  70:        while (i < k)
  71:        {
  72:          // move the i index to the k until something is found that is
  73:          //  greater than the pivot
  74:          while ((array[i].CompareTo(pivot) <= 0) && (i < k))
  75:          {
  76:            i++;
  77:          }
  78:   
  79:          // move the k index to the i until something is found that is
  80:          //  less than the pivot
  81:          while ((pivot.CompareTo(array[k]) <= 0) && (i < k))
  82:          {
  83:            k -= 1;
  84:          }
  85:   
  86:          if (i < k)
  87:          {
  88:            // swap i and k
  89:            Swap(array, i, k);
  90:          }
  91:        }
  92:   
  93:        // put the pivot back
  94:        array[right] = array[k];
  95:        array[k] = pivot;
  96:   
  97:        // sort the left side
  98:        Introsort(array, left, i - 1, depthLimit);
  99:   
 100:        // sort the right side
 101:        Introsort(array, k + 1, right, depthLimit);
 102:   
 103:      }
 104:   
 105:      public void Quicksort (T[] array, int left, int right)
 106:      {
 107:        if (left >= right)
 108:        {
 109:          // done sorting
 110:          return;
 111:        }
 112:        else if (left == right - 1)
 113:        {
 114:          // two element list
 115:          if (array[left].CompareTo(array[right]) > 0)
 116:          {
 117:            // left is greater than the right — swap them
 118:            Swap(array, left, right);
 119:          }
 120:   
 121:          // done sorting
 122:          return;
 123:        }
 124:   
 125:        // since this method is recursive avoid placing these variables on the
 126:        //   stack until the last moment
 127:        int i = left;
 128:        int k = right;
 129:   
 130:        // choose a pivot and move it to the end of the list
 131:        int pivotIndex = (left + right) >> 1; // (left + right) / 2
 132:        T pivot = array[pivotIndex];
 133:        array[pivotIndex] = array[right];
 134:        array[right] = pivot;
 135:   
 136:        while (i < k)
 137:        {
 138:          // move the i index to the k until something is found that is
 139:          //  greater than the pivot
 140:          while ((array[i].CompareTo(pivot) <= 0) && (i < k))
 141:          {
 142:            i++;
 143:          }
 144:   
 145:          // move the k index to the i until something is found that is
 146:          //  less than the pivot
 147:          while ((pivot.CompareTo(array[k]) <= 0) && (i < k))
 148:          {
 149:            k -= 1;
 150:          }
 151:   
 152:          if (i < k)
 153:          {
 154:            // swap i and k
 155:            Swap(array, i, k);
 156:          }
 157:        }
 158:   
 159:        // put the pivot back
 160:        array[right] = array[k];
 161:        array[k] = pivot;
 162:   
 163:        // sort the left side
 164:        Quicksort(array, left, i - 1);
 165:  
 166:        // sort the right side
 167:        Quicksort(array, k + 1, right);
 168:      }
 169:   
 170:      public void Heapsort (T[] array, int start, int end)
 171:      {
 172:        // to begin, place the array in max-heap order
 173:   
 174:        // get the index in array of the last parent node
 175:        int left = start + ((end - start) >> 1);
 176:        int right = end;
 177:        int offset = start;
 178:   
 179:        //while (left >= 0)
 180:        while (left >= start)
 181:        {
 182:          // sift down the node at index left to the proper place so that all
 183:          //   nodes below the left index are in heap order
 184:          //SiftDown(array, left, array.Length - 1);
 185:          SiftDown(array, offset, left, end);
 186:          left -= 1;
 187:        }
 188:   
 189:        // all nodes should now be in max-heap order
 190:   
 191:        while (right > start)
 192:        {
 193:          // swap the maximum value of the heap with the last element of the
 194:          //   heap
 195:          Swap(array, right, start);
 196:   
 197:          // decrease the size of the heap by one so that the previous max
 198:          //   value will stay in its proper placement
 199:          right -= 1;
 200:   
 201:          // put the heap back in max-heap order
 202:          SiftDown(array, offset, start, right);
 203:        }
 204:      }
 205:   
 206:      private void SiftDown (T[] array, int offset, int start, int end)
 207:      {
 208:        int root = start;
 209:        int child;
 210:   
 211:        // keep going while the root has at least one child
 212:        while (((root - offset) * 2 + 1) <= (end - offset))
 213:        {
 214:          // get the left child
 215:          child = ((root - offset) * 2 + 1) + offset;
 216:   
 217:          // if the child has a sibling and the child’s value is less than its
 218:          //   siblings
 219:          if (child < end && array[child].CompareTo(array[child + 1]) < 0)
 220:          {
 221:            // point to the right child instead
 222:            child += 1;
 223:          }
 224:          if (array[root].CompareTo(array[child]) < 0)
 225:          {
 226:            // out of max-heap order
 227:            Swap(array, root, child);
 228:   
 229:            // repeat to continue sifting down the child
 230:            root = child;
 231:          }
 232:          else
 233:          {
 234:            // all sifted
 235:            break;
 236:          }
 237:        }
 238:      }
 239:   
 240:      private void Swap (T[] array, int i, int k)
 241:      {
 242:        T swap = array[i];
 243:        array[i] = array[k];
 244:        array[k] = swap;
 245:      }
 246:    }
 247:  }

  1. 1 Trackback(s)

  2. Jun 3, 2008: C# Heapsort » Pherg.net

Post a Comment