快速排序
基本思想
快速排序(Quicksort)是对冒泡排序的一种改进。它由C. A. R. Hoare在1960年提出,是一种二叉树结构的交换排序方法。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,设立一个关键字,其中一部分的所有数据都比关键字小,另外一部分的所有数据都比关键字大,然后再按此方法对这两部分数据分别进行快速排序,直到达到整个数据变成有序序列。
排序过程
快速排序的实现有三种方法:
-
左右指针法
以升序为例,首先定义两个指针left和right,再定义一个指向关键字的指针key。
第一趟排序时,将left指向第一个元素即最左边,right指向倒数第二个元素即右数第二个元素,key指向最后一个元素,然后left向右走,right向左走,依次移动一个元素的位置,left先走,当left遇见一个比key指向元素大的元素时停下,即指向该元素,然后right向左走,直到遇见一个比key指向元素小的元素时停下,然后交换left和right指向的两个元素,再重复进行这个过程,直到两个指针相遇,然后交换相遇点的元素和key指向的元素。这样就完成了key左边的元素均比它小,而key右边的元素均比它大的目的。
然后以最后的相遇点作为分界点,将整个待排序序列分为两部分,每部分同样进行上述操作,直到不能再继续分组为止,这时便完成了排序操作。
-
挖坑法
挖坑法与左右指针法较为相似,同样以升序为例,首先定义两个指针left和right,再定义一个记录关键字的key。
第一趟排序时,将left指向第一个元素即最左边,right指向最后一个元素即最右边,取最后一个元素作为key的值,然后left向右走,right向左走,依次移动一个元素的位置,left先走,当left遇见一个比key大的元素时停下,即指向该元素,并将left指向的元素赋值给right指向的位置;然后right向左走,直到遇见一个比key小的元素时停下,并将right指向的元素赋值给left指向的位置;再重复进行这个过程,直到两个指针相遇,然后将相遇点位置赋值为key的值。这样就完成了key左边的元素均比它小,而key右边的元素均比它大的目的。
然后以最后的相遇点作为分界点,将整个待排序序列分为两部分,每部分同样进行上述操作,直到不能再继续分组为止,这时便完成了排序操作。
-
前后指针法
同样以升序为例,这种方法首先定义了两个指针prev和cur,在定义一个只想关键字的key。
第一趟排序时,令key指向最后一个元素作为关键字,cur指向第一个元素,prev则指向cur前面即左边挨着的一个元素。在cur小于key时,即cur没有和key指向同一个位置,cur向右走,一次走一个元素的距离,如果cur指向的元素小于key,那么prev也跟随cur向右走一个元素的距离,同时如果cur指向的元素小于key且prev指向的的下一个元素不是cur,那么交换prev和cur各自指向的元素。即当cur指向的元素小于key关键字时,prev始终紧挨着cur,指向cur前面即左边挨着的一个元素,但如果cur遇见了大于key关键字的元素,prev会停留在cur左边第一个比key关键字大的元素的左边紧挨着的一个元素的位置。重复上述过程,直到cur遇见key关键字的位置,然后prev再向右走一个位置,再次交换prev和cur各自指向的元素,此时完成第一趟排序,也即完成了key左边的元素均比它小,而key右边的元素均比它大的目的。
然后以prev的位置作为分界点,将整个待排序序列分为两部分,每部分同样进行上述操作,直到不能再继续分组为止,这时便完成了排序操作。
关于快速排序的优化- 三数取中法优化
上述算法采用将区间的最后一个元素作为关键字进行进一步的排序,但为了防止出现每次选择到最值元素这种最坏情况,引入三数取中法对算法进行优化。
三数取中法是取区间的第一个、最后一个和中间元素相比较,选择出大小居中的元素作为关键字。
引入了三数取中法之后,可以将最坏情况即原始序列有序的情况转换为最好情况,即每一次取到序列的中值元素,即每一次划分完全平均的二分迭代的最好情况。 - 小区间优化
当快速排序采用迭代方式实现时,在区间比较小时改换其他的排序方法进行排序,可以大大减少递归次数,但是很多编译器对于递归本身进行了优化,在实际的测试中这种优化的效果并不明显。
- 三数取中法优化
C语言代码实现
交换函数:
void Swap(int* a, int* b)
{
int t = *a;
*a = *b;
*b = t;
}
三数取中法优化:
int GetMidIndex(int*a, int left, int right)
{
int mid = left + (right - left) / 2;
if (a[left] < a[mid])
{
if (a[mid] < a[right])
return mid;
else if (a[left]