快速排序,简称快排,是一种十分常用的排序算法,一般情形下拥有O(n*logn)的复杂度。
算法思想:
快排的算法思想是在递归的基础上实现的,每次选择待排列序列中的一个数字,一般是待排列序列的第一个数字,作为主元,将大于主元的数字放在右边,小于主元的数字放在左边。递归进行就可以完成排序。
算法伪码:
该算法的伪码有两种典型的写法。先介绍第一种,它出现在严蔚敏老师的《数据结构》中,思想展示如下:
待排序序列 | 4、1、5、2、3 |
第一次 | 主元:4;序列变化:3、1、5、2、3 |
第二次 | 主元:4;序列变化:3、1、5、2、5 |
第三次 | 主元:4;序列变化:3、1、2、2、5 |
第四次 | 主元:4;序列变化:3、1、2、4、5 |
从上述展示过程可以看出,首先选择主元,然后用两个指针分别只想序列的首尾两部,首先末尾指针向前查询,找到第一个小于主元的数字,复制到首部指针处,然后首部指针向后查询,找到第一个大于等于主元的数,复制到尾部指针处。上述过程循环进行,直到两个指针相遇,此称为一趟。代码展示如下:
void QuickSort(int* A,int start,int end)
{
if(start<end)
{
int pivot=Partition(A,start,end);
QuickSort(A,start,pivot-1);
QuickSort(A,pivot+1,end);
}
}
int Partition(int* A,int start,int end)
{
int pivt=A[start];
int tempoint_1=start,tempoint_2=end;
while(tempoint_1<tempoint_2)
{
while(tempoint_1<tempoint_2&&A[tempoint_2]>pivt) tempoint_2--;
if(tempoint_1<tempoint_2)
{
A[tempoint_1]=A[tempoint_2];
tempoint_1++;
}
while(tempoint_1<tempoint_2&&A[tempoint_1]<pivt) tempoint_1++;
if(tempoint_1<tempoint_2)
{
A[tempoint_2]=A[tempoint_1];
tempoint_2--;
}
}
A[tempoint_1]=pivt;
return tempoint_1;
}
另一种代码实现广泛出现于各种算法教材中,它在殷人昆老师的《数据结构(用面向对象方法与C++语言描述)》也有体现,下面给出该算法的实现步骤:
第一趟排序
待排序序列 | 4、1、5、2、3 |
第一次 | 主元:3;序列变化:1、4、5、2、3 |
第二次 | 主元:3;序列变化:1、4、5、2、3 |
第三次 | 主元:3;序列变化:1、2、5、4、3 |
第四次 | 主元:3;序列变化:1、2、3、4、5 |
它以末尾元素作为主元进行操作,算法复杂度仍然为O(n*logn)。代码展示如下:
void QuickSort(int* A,int start,int end)
{
if(start<end)
{
int pivot=Partition(A,start,end);
QuickSort(A,start,pivot-1);
QuickSort(A,pivot+1,end);
}
}
int Partition(int* A,int start,int end)
{
int pviot=A[end];
int i=start-1;
for(int j=start;j<end;j++)
{
if(A[j]<=pviot)
{
i++;
swap(A[j],A[i]);
}
}
swap(A[i+1],A[end]);
return i+1;
}
算法分析:
从上面的分析可以看出,数组的划分对于算法的效率将起到极大的作用。在已经有序的序列中,若每次选择头元素或者尾元素算法的效率将达到最差O(n^2)。这时我们不希望看到的。但是令人欣慰的是,快排一般而言效果总是好的。因此C++中基于快排内核编写一个内置函数:sort。
sort函数的用法相当简单,而且由于C++中提供了模板类,因而该函数可以对多种结构完成排序任务。
例如:
对于数组A[20](int),若要对其中数字进行排序,可以调用sort(A,A+20)。
对于结构体,若要进行排序,那么有两种方法可以供选择,一种是使用C++的重载方法,给出结构体的大小比较方法;二是使用sort函数的内置方法。
参考文献:
1.数据结构. 严蔚敏. 清华大学出版社
2.数据结构. 殷人昆. 清华大学出版社
3.算法导论. Thomas H.Cormen等. 机械工业出版社