Home  |  FAQ  |  About  |  Contact  |  View Source   
 
SEARCH:
 
BROWSE:
    My Hood
Edit My Info
View Events
Read Tutorials
Training Modules
View Presentations
Download Tools
Scan News
Get Jobs
Message Forums
School Forums
Member Directory
   
CONTRIBUTE:
    Sign me up!
Post an Event
Submit Tutorials
Upload Tools
Link News
Post Jobs
   
   
Home >  Tutorials >  C# >  QuickSort in C#
Add to MyHood
   QuickSort in C#   [ printer friendly ]
Stats
  Rating: 4.42 out of 5 by 12 users
  Submitted: 11/05/02
Ben Watson ()

 
QuickSort in C#
by Ben Watson

Introduction

This program stems from an assignment I did for a class. In every day use, there is little reason to write our own sorting routines because there are so many other, highly-optimized implementations at our disposal. However, understanding one of the fastest and most widely used sorting algorithms out there is sure to benefit any but the most casual programmer.

General Procedure

QuickSort is designed to work efficiently on arrays, and it is based on a divide & conquer approach. The general steps are as follows:

1. Choose a "pivot" location in the array.
2. Move everything less than the pivot to the left part of the array, and everything more (or equal) than it to the right part of the array.
3. Move the pivot item to where we stopped scanning for items less than it.
4. Call quicksort recursively on the parts of the array to either side of the pivot.
5. When the part of the array we're sorting is sufficiently small, call a simpler sorting algorithm on the few places left, and return.

Walkthrough example

This is easier to see with an example:

Let's sort the following array of ten integers:

[6 24 80 4 19 84 1 10 13 7]

Let's choose our pivot to arbitrarily be the first 
element, a 6. In addition, let's create two 
pointer iterators to walk through the array looking 
for things to swap--one from the beginning and the 
other from the end. We'll call these variable p, m, 
and n.

Thus:

 p
[6 24 80 4 19 84 1 10 13 7]
   m                     n

If array[m] is greater than array[p] and array[n] is 
less than array[p] we'll simply swap array[m] and 
array[n]. If one of the conditions matches, and 
the other doesn't we'll just keep moving the pointers 
until we can do a swap:

 p
[6 24 80 4 19 84 1 10 13 7]
   m             n        

Swap:

 p
[6 1 80 4 19 84 24 10 13 7]
   m             n        

Advance pointers:

 p
[6 1 80 4 19 84 24 10 13 7]
     m  n

Swap again:

 p
[6 1 4 80 19 84 24 10 13 7]
     m  n

Advance pointers:

 p
[6 1 4 80 19 84 24 10 13 7]
     n m

Now we see that our pointers have crossed each other. 
At this point we can safely move our pivot to where n is. 
This will put the pivot item in its final position in 
the sorted array:

     p
[4 1 6 80 19 84 24 10 13 7]


At this point, everything before p is less than array[p] 
and everything after p is greater that array[p]. Now 
we just need to sort both halves. To sort the first half, 
we don't need to make a call to quicksort--that would be 
a waste of time since it's only a few elements in length. 
Instead, we can use a more elementary sorting algorithm, 
like selection sort, which will yield the following array:

     p     
[1 4 6 80 19 84 24 10 13 7]

we also need to sort the array after p. For this we 
will call quicksort recursively on that part. I'll rewrite 
the array to show only what we're interested in and we'll 
again choose the first element as the pivot:

 p     
[80 19 84 24 10 13 7]
       m           n

I've already moved m to the first element greater 
than array[p], and the last element less than array[p] 
is the last element in the array. We can then swap those:

 p     
[80 19 7 24 10 13 84]
       m           n

You'll notice that there are no more swaps that will occur 
in this step, and our pointers will cross like this:

 p     
[80 19 7 24 10 13 84]
               n  m

We can then swap p and n, giving us:

               p     
[13 19 7 24 10 80 84]

We obviously do not need to sort the right hand side, 
but the left hand could use a little work:

 p                   
[13 19 7 24 10]
    m       n
               
 p                   
[13 10 7 24 19]
    m       n

 p                   
[13 10 7 24 19]
       n m

       p                   
[7 10 13 24 19]


Each side of this can be easily sorted using selection 
sort (or similar), giving:

[7 10 13 19 24]

If then look at the whole array we've sorted in pieces, we'll have:

[1 4]6[7 10 13 19 24]80[84] -- perfectly sorted!


Make sure you understand the walk-through above, and if you're having trouble understanding what's going--work out your own array on paper--just like I did when I learned this.

Code

To implement this in C#, let's create our own Array class:
public class Array
{
    int[] array;
    int numItems;

    public Array(int size) 
    {
        array=new int[size];
        numItems=0;
    }
    public void Add(int i) 
    {
        array[numItems++]=i;
    }
}


This is just the basic plumbing to store our values.

We need to add a function to do our pivoting, or swapping:


    private int Pivot(int beg, int end)
    {
        //set pivot to beginning of array
        int p=array[beg];
        //m also starts at beginning
        int m=beg;
        //n starts off end (we'll decrement it before it's used)
        int n=end+1;
        do 
        {
            m=m+1;
        } while (array[m]<=p && m<end);//find first larger element
        do 
        {
            n=n-1;
        } while (array[n] >p);//find last smaller element
        //loop until pointers cross
        while (m<n) 
        {
            //swap
            int temp=array[m];
            array[m]=array[n];
            array[n]=temp;
            //find next values to swap
            do 
            {
                m=m+1;
            } while (array[m]<=p);
            do 
            {
                n=n-1;
            } while (array[n] >p);
        }
        //swap beginning with n
        int temp2=array[n];
        array[n]=array[beg];
        array[beg]=temp2;
        return n;// n is now the division between two unsorted halves
    }


Pivot() does exactly what I described in the walkthrough. The comments should help you through the code.

We also need a function that does our "simple" sorting. I've decided to use selection sort:

    private void SelectionSort(int beg, int end) 
    {
        for (int i=beg;i<end;i++) 
        {
            int minj=i;        
            int minx=array[i];
            for (int j=i+1;j<=end;j++) 
            {
                if (array[j]<minx) 
                {
                    minj=j;
                    minx=array[j];
                }                
            }
            array[minj]=array[i];
            array[i]=minx;
              
        }
    }


Now that we have our basic pieces, we can create some controlling functions:

    public void Sort() 
    {
        QuickSort(0,array.numItems-1);
    }
    private void QuickSort(int beg, int end) 
    {
        if (end==beg) return;
        if (end-beg<9) //an arbitrary limit to when we call selectionsort
            SelectionSort(beg,end);
        else 
        {
            int l=Pivot(beg,end);
            QuickSort(beg,l-1);
            QuickSort(l+1,end);
        }
    }


We make Sort() our publically accessible method, which calls QuickSort() on the entire array. QuickSort makes sure there is enough array to run on. If there are few elements, it calls SelectionSort(), otherwise, it pivots the array, and calls itself on each half.

I also created a public method to dump the entire array to the screen so that you can see the results.
    public void Dump() 
    {
        foreach(Object o in array)
            System.Console.Write(o.ToString()+ " ");
        System.Console.WriteLine("");
    }


That's it! It's pretty easy once you understand it. There are likely a number of things you can do to optimize the algorithm even further, but this is the basic format.

Just to test
Use the following code to demonstrate the Array class and QuickSort:

class MainClass
    {
    [STAThread]
    static int Main(string[] args)
    {
        if (args.Length==0) 
        {
            Console.WriteLine("You must enter an integer to specify array size.");
            return 1;
        }
        int size=Convert.ToInt32(args[0]);
        Array array=new Array(size);
        Random r=new Random();
        for (int i=0;i<size;i++)
            array.Add(r.Next(size));
        array.Dump();
        array.Sort();
        array.Dump();
        return 0;
    }
}





Considerations

QuickSort runs in O(n log n) time, which is plenty quick, however there are two cases which will absolutely kill its performance. If the array is sorted in either ascending or descending order, then pivot will need to be called on every single element (try running through an example if you can't see why that is). Therefore, QuickSort is really only optimal for arrays in mostly random order.

If it were possible to select the median as the pivot every time, we could always get optimal running time, however this turns out to be a problem that is not worth solving in most instances. Picking a random element, or always the first, etc. will usually be sufficiently fast.

Return to Browsing Tutorials

Email this Tutorial to a Friend

Rate this Content:  
low quality  1 2 3 4 5  high quality

Reader's Comments Post a Comment
 
Nice tutorial, Ben. *****
-- BYU FAN, November 07, 2002
 
I was just thinking to myself to write this same tutorial while I was in CS class. No need now though, you've definately done a better job of explaining it then I ever could, and the C# code is clean and easy to read. Nice job! *****
-- George Schwenzfeger, November 08, 2002
 
QuickSort runs in O(n log n) time

Actually... it's best running time is O(nlogn). Worst case is O(n^2). Imagine the case where the pivot is always the minimal element.

Not a big deal, but it should be pointed out, if you want guaranteed runtime then you have to be a bit more careful about your pivot selection, and if you want to guarantee O(nlogn) then you should always pick your pivot to be the median of the set you are currently sorting.
-- John Gallardo, November 08, 2002
 
Very good tutorial, clear and clean explanation of QuickSort. Very easy to read code...
-- Greg McMurray, November 08, 2002
 
John, many those details are pointed out in the closing paragraphs of the tutorial. However, I did not mention the worst case being O(n^2) which happens when the array is sorted, so thanks for pointing that out.
-- Ben Watson, November 09, 2002
 
Very nice tutorial, 5 stars.

Just a few things though:
If you had used a random pivot instead of the first element, then you would make it very very unlikely to reach the worst case of O(n^2).

If you really want to enforce O(nlogn), you can easily use heapsort. Though the constant is slightly more than that of a tuned quicksort.
-- Larry Mak, November 15, 2002
 
Nice, however, no need to allocate temp variable on the stack just to swap two values.
Thus:

int temp=array[m];
array[m]=array[n];
array[n]=temp;

Can be written as:

array[m] ^= array[n];
array[n] ^= array[m];
array[m] ^= array[n];

--
Regards,
David Pruidze
-- David Pruidze, December 18, 2002
 
nlogn tutorial =)
-- @ariel [], April 02, 2003
 
David,

In the current model of memory, as long as the variable is small enough, the actual allocation of memory is actually faster than using XOR to do swapping. At least this is usually the case in C with ints..
-- Larry Mak, April 21, 2003
 
Copyright © 2001 DevHood® All Rights Reserved