一、基本思想:
通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,
然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
Partition 是快排的灵魂 , 函数实现了,选择一个主元,主元将数组分为两部分,左边部分小于主元;右边部分大于主元。
二、Partition的两种实现
1. 从头到尾遍历
从头到尾遍历,和主元比较,大的不管,小的换。
参考https://www.cnblogs.com/AlgrithmsRookie/p/5899160.html
int Partition1(int a[], int low, int high)
{
int x = a[high];//将数组最后一个数作主元,用它来对数组进行划分
//也可以随机选一个作主元,再和最后一个交换
//int index = RandomINRange(low,high);
//int x = a[index];
//swap(a[index],a[high]);
int i = low;
for(int j=low;j<high;j++)//high不取等
{
if(a[j]<x)//从头到尾,和主元比较,大的不管,小的换
swap(a[j],a[i++]);
}
swap(a[i],a[high]);//把主元放到i的位置
return i;
}
2. 两头往中间扫
参考https://blog.csdn.net/AA2519556/article/details/77884962
取区间第一个数为基准数。先从后向前找,再从前向后找
int Partition2(int a[], int low, int high)
{
int x = a[low];//第一个做主元
while(low<high)
{
while(a[high]>=x && low<high)high--;//找到当前右边第一个比key小的值
a[low] = a[high]; //不用写成交换两个数,直接赋值
while(a[low]<=x && low<high)low++;
a[high] = a[low];
}
a[low] = x;//跳出循环时low=high
return low;
}
递归实现快排
void Quicksort(int a[],int low, int high)
{
if(low<high)
{
int q = Partition2(a,low,high);
Quicksort(a,low,q-1);
Quicksort(a,q+1,high);
}
}
三、优化
1. 选轴
)选用待排数组最左边、最右边和最中间的三个元素的中间值作为中轴。
)随机选取中轴
2. 根据分区大小调整算法
快速排序对于小规模的数据集性能不是很好
当分区的规模达到一定小时,便停止快速排序算法
也即快速排序算法的最终产物是一个“几乎”排序完成的有序数列。数列中有部分元素并没有排到最终的有序序列的位置上,但是这种元素并不多。可以对这种“几乎”完成排序的数列使用插入排序算法进行排序以最终完成整个排序过程。
插入排序对于这种“几乎”完成的排序数列有着接近线性的复杂度。
3. 区间三分
当要分区的所有的元素值都相等时,一般的快速排序算法就陷入了最坏的一种情况,也即反复的交换相同的元素并返回最差的中轴值。无论是任何数据集,只要它们中包含了很多相同的元素的话,这都是一个严重的问题,因为许多“底层”的分区都会变得完全一样。
对于这种情况的一种改进办法就是将分区分为三块而不是原来的两块:一块是小于中轴值的所有元素,一块是等于中轴值的所有元素,另一块是大于中轴值的所有元素。另一种简单的改进方法是,当分区完成后,如果发现最左和最右两个元素值相等的话就避免递归调用而采用其他的排序算法来完成。
4. 并行
由于快速排序算法是采用分治技术来进行实现的,这就使得它很容易能够在多台处理机上并行处理。