声明:引用请注明出处http://blog.csdn.net/finish_dream/article/details/70207992
引言
交换排序中有两种具体排序方法,分别是:冒泡排序,快速排序。上一篇中已经介绍了冒泡排序,这篇文章将主要介绍快速排序。
排序相关的基本概念
排序:将一组杂乱无章的数据按一定的规律顺次排列起来。
- 数据表(data list):它是待排序对象的有限集合。
- 排序码(key):通常对象有多个属性域,即多个数据成员组成,其中有一个属性域,可用来区分对象,作为排序依据。该域即为排序
码,每个数据表用哪个属性域作为排序码,要视具体的应用需要而定。
分类
- 内排序:指在排序期间数据对象全部存放在内存的排序;
- 外排序:指在排序期间全部对象个数太多,不能同时存放在内存,必须根据排序过程的要求,不断在内、外存之间移动的排序。
排序的分析
排序算法的稳定性
如果在对象序列中有两个对象r[i]和r[j] ,它们的排序码k[i]==k[j] 。如果排序前后,对象r[i]和r[j] 的相对位置不变,则称排序算法是稳定的;否则排序算法是不稳定的。
排序算法的评价
时间开销
- 排序的时间开销可用算法执行中的数据比较次数与数据移动次数来衡量。
- 算法运行时间代价的大略估算一般都按平均情况进行估算。对于那些受对象排序码序列初始排列及对象个数影响较大的,需要按最好情况和最坏情况进行估算。
空间开销
算法执行时所需的附加存储。
快速排序
基本思想
- 任选一个记录,以其关键字为“枢轴”,序列中比它小的移动到该记录之前,反之移动到它之后。
- 通过一趟排序将待排的记录分割成两个区域,一个区域中记录的关键字比另一个区域的关键字小(一次划分)。
- 然后左右两边分别处理进行相同的操作直到排好。
快速排序是一个递归算法,平均时间复杂度为O(nlog(n)),inplace操作,需要最小的额外内存。
C#代码
/// <summary>
/// 直接插入排序
/// </summary>
/// <param name="Array">待排序数组</param>
/// <returns></returns>
private static void InsertSort(ref int[] array, int left, int right)
{
int num = right - left + 1; //数组元素个数
int tmp, j;
for (int i = left + 1; i < left + num; i++) //外层控制
{
tmp = array[i];
for (j = i; j > left && array[j - 1] > tmp; j--) //内层控制
{
array[j] = array[j - 1];
}
array[j] = tmp;
}
}
/// <summary>
/// 用三值中分法求枢纽元
/// </summary>
/// <param name="array">数组</param>
/// <param name="left">该分组左侧值</param>
/// <param name="right">该分组右侧值</param>
/// <returns></returns>
private static int Median3(ref int[] array, int left, int right)
{
int center = (left + right) / 2;
//init array[left]<=array[center]<=array[right]
if (array[left] > array[center])
Swap(ref array[left], ref array[center]);
if (array[left] > array[right])
Swap(ref array[left], ref array[right]);
if (array[center] > array[right])
Swap(ref array[center], ref array[right]);
Swap(ref array[center], ref array[right - 1]);
return array[right - 1];
}
/// <summary>
/// 快速排序算法
/// </summary>
/// <param name="array">待排序数组</param>
/// <param name="left">分组最左侧值</param>
/// <param name="right">分组最右侧值</param>
private static void QSort(ref int[] array, int left, int right)
{
if (left + 10 <= right)
{
int pivot = Median3(ref array, left, right);//枢纽元
int i = left, j = right - 1;
while (true)
{
while (array[++i] < pivot) { }
while (array[--j] > pivot) { }
if (i < j)
Swap(ref array[i], ref array[j]);
else
break;
}
Swap(ref array[i], ref array[right - 1]);
QSort(ref array, left, i - 1);
QSort(ref array, i + 1, right);
// return array;
}
else if (left <= right)
{
InsertSort(ref array, left, right);
}
else
{
throw new Exception("待排序元素个数大小不能小于0");
}
}
/// <summary>
/// 快速排序
/// </summary>
/// <param name="array">待排序数组</param>
/// <returns></returns>
public static int[] QuickSort(int[] array)
{
if (array.Length > 0)
{
QSort(ref array, 0, array.Length - 1);
return array;
}
else
{
throw new Exception("待排序数组大小不能小于0");
}
}
算法分析
最好情况:每次划分所取的基准都是当前无序区的”中值”记录,划分的结果是基准的左、右两个无序子区间的长度大致相等。总的关键字比较次数:0(nlgn)。
最坏情况:每次划分选取的基准都是当前无序区中关键字最小(或最大)的记录,划分的结果是基准左边的子区间为空(或右边的子区间为空),而划分所得的另一个非空的子区间中记录数目,仅仅比划分前的无序区中记录个数减少一个。因此,快速排序必须做n-1次划分,第i次划分开始时区间长度为n-i+1,所需的比较次数为n-i(1≤i≤n-1),故总的比较次数达到最大值:n(n−1)/2=O(n2)。
因为快速排序的记录移动次数不大于比较的次数,所以快速排序的最坏时间复杂度应为0(n2),最好时间复杂度为O(nlgn)。但就平均性能而言,它是基于关键字比较的内部排序算法中速度最快者,快速排序亦因此而得名。它的平均时间复杂度为O(nlgn)。
快速排序是非稳定的。
完整代码被我放在了Github上,感兴趣的可以下下来看一下https://github.com/Finish-Dream/DSAlgorithm