快排思想:快速排序由C. A. R. Hoare在1962年提出。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。(采用了分治思想)
时间复杂度:
假若要求从小到大,并且选取key为中间值
最好也就是顺序:O(nlogn)
最坏也就是逆序:O(n^2)
——————————————————————————————————
假若key为第一个元素
最好顺序但第一个元素正好为数组顺序后的中间元素:O(nlogn)
最坏就是顺序即,key为最小元素:O(n^2)
例子:int a[]={1,4,3,2,1,6,0,9,8,7}
步骤:
1.我们***取中间值为key***,将数组分成两半,如果start>=right就结束或者a数组为空也结束方法return。
2.用两个变量一个从数组头部开始,一个从数组尾部开始。即i= start ; j = end。
3.while(i<j) 也就是i与j相遇则一次快排结束,并终止while循环。
4.判断中间值左边的当前数是否 <= 中间数 && 下标 < 中间数下标,是则i++,否则进行下一步
5.判断中间值右边的当前数是否 >= 中间数 && 下标 > 中间数下标,是则j–,否则进行下一步
6.判断当前i和j的情况:
- ①i==j为true:则一次快排结束break;
- ②i,j都不为中间值下标,说明一次快排并没有结束并且找到了不满足条件的i,j,交换它们
- ③i==中间值下标为true,说明key左边的值都 <= key ,并且右边存在<key的数,所以我们需要把<key的这个数放到key位置,并把key位置右移一位
- ④j==中间值下标为true,说明key右边的值都 >= key ,并且左边存在>key的数,所以我们需要把>key的这个数放到key位置,并把key位置左移一位
7.回到while循环
8.递归左边部分a,left,mid-1
9.递归右边部分a,mid+1,right
过程:
原数据:1,4,3,2,[1],6,0,9,8,7
一次排序后:{1,0,1},[2],{3,6,4,9,8,7}
两次快排后:[0],{1,1},2,{3,6,4,7},[8],{9}
三次快排后:0,1,1,2,{3,4},[6],{7},8,9
四次快排后:0,1,1,2,3,4,6,7,8,9
代码:
/**
* Created by 96274 on 2018/11/20.
*/
public class fastSortTest {
public static void quickSort(int a[], int left ,int right){
// System.out.println("left="+left+",right="+right);
int mid ;
mid = (left+right)/2;
if(left>=right)
return;
if(a.length<=0){
return;
}
int midValue = a[mid]; //中间值为key
int i = left; //i从左
int j = right; //j从右
while (i<j){ //i与j未相遇过
while(a[i]<= midValue && i<mid){
i++; //找出不符合条件的值,即大于key的值
}
while (a[j]>= midValue && j>mid){
j--; //找出不符合条件的值,即小于key的值
}
if(i==j){
break;
}else if(i!=mid && j!=mid){
swap(a,i,j); //②情况
}else if(i == mid){
swap(a,mid,mid+1); //③情况
mid= mid+1;
}else if(j == mid){
swap(a,mid,mid-1); //④情况
mid= mid-1;
}
}
quickSort(a,left,mid-1); //递归左边
quickSort(a,mid+1,right); //递归右边
}
public static void swap(int a[],int n1,int n2){
int t = a[n1];
a[n1] = a[n2];
a[n2] = t;
}
public static void main(String[] args){
int a[] = {1,3,0,0,0, 0,0,0,0,0};
quickSort(a,0,a.length-1);
for(int i = 0 ; i< a.length; i++){
System.out.print(" "+a[i]);
}
}
}
一些优化快排思想:
三平均分区法:也就是选择第一个,最后一个,和中间值为key进行快排。
好处:①避免最坏情况的出现的机率。②未改进的快速排序算法为了防止比较时数组越界,在最后要设置一个哨点。(这一点我没用到哨点所以并不明白)
根据分区大小调整算法:
这一方面的改进是针对快速排序算法的弱点进行的。快速排序对于小规模的数据集性能不是很好。可能有人认为可以忽略这个缺点不计,因为大多数排序都只要考虑大规模的适应性就行了。但是快速排序算法使用了分治技术,最终来说大的数据集都要分为小的数据集来进行处理。由此可以得到的改进就是,当数据集较小时,不必继续递归调用快速排序算法,而改为调用其他的对于小规模数据集处理能力较强的排序算法来完成。Introsort就是这样的一种算法,它开始采用快速排序算法进行排序,当递归达到一定深度时就改为堆排序来处理。这样就克服了快速排序在小规模数据集处理中复杂的中轴选择,也确保了堆排序在最坏情况下O(n log n)的复杂度。
另一种优化改进是当分区的规模达到一定小时,便停止快速排序算法。也即快速排序算法的最终产物是一个“几乎”排序完成的有序数列。数列中有部分元素并没有排到最终的有序序列的位置上,但是这种元素并不多。可以对这种“几乎”完成排序的数列使用插入排序算法进行排序以最终完成整个排序过程。因为插入排序对于这种“几乎”完成的排序数列有着接近线性的复杂度。这一改进被证明比持续使用快速排序算法要有效的多。
另一种快速排序的改进策略是在递归排序子分区的时候,总是选择优先排序那个最小的分区。这个选择能够更加有效的利用存储空间从而从整体上加速算法的执行。
不同的分区方案考虑:
对于快速排序算法来说,实际上大量的时间都消耗在了分区上面,因此一个好的分区实现是非常重要的。尤其是当要分区的所有的元素值都相等时,一般的快速排序算法就陷入了最坏的一种情况,也即反复的交换相同的元素并返回最差的中轴值。无论是任何数据集,只要它们中包含了很多相同的元素的话,这都是一个严重的问题,因为许多“底层”的分区都会变得完全一样。
对于这种情况的一种改进办法就是将分区分为三块而不是原来的两块:一块是小于中轴值的所有元素,一块是等于中轴值的所有元素,另一块是大于中轴值的所有元素。另一种简单的改进方法是,当分区完成后,如果发现最左和最右两个元素值相等的话就避免递归调用而采用其他的排序算法来完成。
并行的快速排序:
由于快速排序算法是采用分治技术来进行实现的,这就使得它很容易能够在多台处理机上并行处理。
在大多数情况下,创建一个线程所需要的时间要远远大于两个元素比较和交换的时间,因此,快速排序的并行算法不可能为每个分区都创建一个新的线程。一般来说,会在实现代码中设定一个阀值,如果分区的元素数目多于该阀值的话,就创建一个新的线程来处理这个分区的排序,否则的话就进行递归调用来排序。
对于这一并行快速排序算法也有其改进。该算法的主要问题在于,分区的这一步骤总是要在子序列并行处理之前完成,这就限制了整个算法的并行程度。解决方法就是将分区这一步骤也并行处理。改进后的并行快速排序算法使用2n个指针来并行处理分区这一步骤,从而增加算法的并行程度。