C#排序算法之快速排序

排序规则:

  1、从数列中挑出一个元素称为“基准”(privot)(一般用数组的第一个位置);

  2、重新排序数列,所有元素比基准小的摆放在基准前面,所有元素比基准大的摆放在基准后面(相同的数可以放到任一边)。在这个分区退出后,该基准就处于数列的中间位置。这个称为分区(partition)操作。

  3、递归地(recursive)把小于基准元素的子数列和大于基准值元素的子数列排序

时间复杂度:

  平均时间复杂度:O(nlogn) 最好情况:O(nlogn); 最坏情况: O(n^2)

namespace 排序算法
{
    class QuickSort1
    {
        public static void Sort(int[] arr)
        {
            int n = arr.Length;
            Sort(arr, 0, n - 1);
        }
        private static void Sort(int[] arr, int l, int r)
        {
            if(r - l + 1 <=15 )
            {
                InsertSort(arr, l, r);
                return;
            }
            //if(l >= r)
            //{
            //    return;
            //}
            //标准元素
            int v = arr[l];
            int j = l; // arr[1+l,2+l,...,j] < v  arr[j+1....,i-l] >v
            for(int i = l + 1; i <= r; i++)
            {
                if(arr[i] < v)
                {
                    j++;
                    Swap(arr, i, j);
                }
            }
            Swap(arr, l, j);
            Sort(arr, l, j - 1);
            Sort(arr, j + 1, r);
        }
        private static void Swap(int[] arr, int i, int j)
        {
            int e = arr[i];
            arr[i] = arr[j];
            arr[j] = e;
        }

        //插入排序
        public static void InsertSort(int[] arr, int l, int r)
        {
            for (int i = l + 1; i <= r; i++)
            {
                int e = arr[i];
                int j;
                for (j = i; j > l; j--)
                {
                    if (e < arr[j - 1])
                    {
                        arr[j] = arr[j - 1];
                    }
                    else
                    {
                        break;
                    }
                }
                arr[j] = e;
            }
        }
    }
}

上面的代码由于基准元素使用的是数组中的第一个,当数组近乎有序时,排序时造成栈的深度会很深(甚至当数组很大时可能会造成栈溢出),而且消耗时间会很多时间复杂度接近O(n^2)

优化:首先基准元素由数组中第一个元素改为随机设定基准元素

using System;
namespace 排序算法
{
    /*
     * 优化快速排序:
     *      基准元素由数组中第一个元素改为随机设定基准元素
     */
    class QuickSort2
    {
        private static Random rd = new Random();
        public static void Sort(int[] arr)
        {
            int n = arr.Length;
            Sort(arr, 0, n - 1);
        }
        private static void Sort(int[] arr, int l, int r)
        {
            if(r - l + 1 <=15 )
            {
                InsertSort(arr, l, r);
                return;
            }
            //if(l >= r)
            //{
            //    return;
            //}
            //基准元素
            int p = l + rd.Next(r - l + 1);
            Swap(arr, l, p);
            int v = arr[l];
            int j = l; // arr[1+l,2+l,...,j] < v  arr[j+1....,i-l] >v
            for(int i = l + 1; i <= r; i++)
            {
                if(arr[i] < v)
                {
                    j++;
                    Swap(arr, i, j);
                }
            }
            Swap(arr, l, j);
            Sort(arr, l, j - 1);
            Sort(arr, j + 1, r);
        }
        private static void Swap(int[] arr, int i, int j)
        {
            int e = arr[i];
            arr[i] = arr[j];
            arr[j] = e;
        }

        //插入排序
        public static void InsertSort(int[] arr, int l, int r)
        {
            for (int i = l + 1; i <= r; i++)
            {
                int e = arr[i];
                int j;
                for (j = i; j > l; j--)
                {
                    if (e < arr[j - 1])
                    {
                        arr[j] = arr[j - 1];
                    }
                    else
                    {
                        break;
                    }
                }
                arr[j] = e;
            }
        }
    }
}

如果数组中含有大量重复元素的数组,还是会造成递归深度很深(甚至会造成栈溢出),时间复杂度也是接近O(n^2)

解决方案:

  使用三向切分的快速排序:将数组切分成三个部分分别对应小于,等于,大于基准元素。

using System;
namespace 排序算法
{
    /*
     * 优化快速排序:
     */
    class QuickSort3
    {
        private static Random rd = new Random();
        public static void Sort(int[] arr)
        {
            int n = arr.Length;
            Sort(arr, 0, n - 1);
        }
        private static void Sort(int[] arr, int l, int r)
        {
            if( r - 1 + 1 <=15)
            {
                InsertSort(arr, l, r);
                return;
            }
            int p = l + rd.Next(r - l + 1);
            Swap(arr, l, p);
            //基准元素
            int v = arr[l];
            int lt = l; // arr[l+1,...,lt] < v
            int gt = r + 1;// arr[gt, ..., r] > v;
            int i = l + 1; // arr[lt + 1,...,i-1] == v
            while( i < gt)
            {
                if(arr[i] < v)
                {
                    lt++;
                    Swap(arr, i, lt);
                    i++;
                }else if( arr[i] > v)
                {
                    gt--;
                    Swap(arr, i, gt);
                    i++;
                }
                else
                {

                    i++;
                }
                 
            }
            Swap(arr, l, lt);
            Sort(arr, l, lt - 1);
            Sort(arr, gt, r);
        }
        private static void Swap(int[] arr, int i, int j)
        {
            int e = arr[i];
            arr[i] = arr[j];
            arr[j] = e;
        }

        //插入排序
        public static void InsertSort(int[] arr, int l, int r)
        {
            for (int i = l + 1; i <= r; i++)
            {
                int e = arr[i];
                int j;
                for (j = i; j > l; j--)
                {
                    if (e < arr[j - 1])
                    {
                        arr[j] = arr[j - 1];
                    }
                    else
                    {
                        break;
                    }
                }
                arr[j] = e;
            }
        }
    }
}

  

  

原文地址:https://www.cnblogs.com/sy-liu/p/13278719.html