算法概述:
快速排序由C. A. R. Hoare在1962年提出。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
是分而治之的典型用例,由于使用了递归,所以对于小规模的数据,速度可能还不如快速排序,所以就需要一个标志位,来判定是否适用快速排序。
具体步骤(例子):
1.选取一个主元,作为分左右两半边的标准,设置左右指针(只是代表位置的移动)
2.左边指针i对应的值应小于主元,若该值大于主元,则该指针不动,把指针切换到右边的j指针。右边的指针同理
把左指针i对应的值(8)和主元(6)比较,8>6,i不动
切换到j指针,对应的值(7)大于6,左移,2<6。所以j不动,此时i和j都停住了,交换值。
交换完成后,左指针切换到i+1(i已排好序),右指针切换到j-1。继续该步骤
3.直到下图,j<i,说明两边都已经排好序,只需要把主元的值和i的值交换即可(j在i的左边,说明i这个位置的元素已经大于主元,所以交换后也符合主元左小右大的规律)。至此该主元的位置确认完成,也是最终的位置(这是为什么快拍效率高的原因,一次排序,作为主元的元素的最终位置已经确认(插入排序等,在插入一次后,不一定是元素的最终位置)。)
快速排序的重点:
元素相同怎么办;
如何选主元;
在何时用快速排序,选择标志位;
接下来解决这三个问题
1.元素相同怎么办?
(1)不管他,指针继续前进
(2)就算元素相同也要交换
对于以上两种情况,在极端情况下看两种如何表现,再进行选择。
例子:元素全部相同的数组。
(1)假设选取末位作为主元,则每次都不交换,则需要和冒泡排序一样,一个接一个遍历完整个数组,时间复杂度降低为O(N^2)。
(2)假设选区末尾作为主元,每次都交换,则每次指针都会交叉到中间,主元会到中间去。起到了平分的作用,从而时间复杂度位O(NLogn)。缺点就是每次都需要交换。
2.如何选主元?
(1)直接选队首或对尾巴
(2)random函数随机选
(3)选区首末中三个的中间值
直接选首末是不合理的
random函数会有额外开销,不合适。
经过他人实验,也是历代总结,人们发现方法(3)选的主元是最快的。就是数组的第一个,最后一个,中间三个值的比较。
3.在何时用快速排序,选择标志位?
由于快速排序使用了递归,而递归在数据小的时候(例:100)的时候,效率还不如简单排序,所以应该该设置一个标志位,当数据量小于该标志位时,应该采用简单排序而非快速排序。
代码实现
主元的选取
/*
主元的选取采用中位数法
选去左中右中的中位数作为主元
*/
public static int Median3(int[] arr,int left,int right){
int center=(left+right)/2;
if (arr[left]>arr[center]){
swap(arr[left],arr[center]);
}
if (arr[center]>arr[right]){
swap(arr[center],arr[right]);
}
if (arr[left]>arr[center]){
swap(arr[left],arr[center]);
}
//此时这三个已经排好了序
//将pivot初始化到右边倒数第二位
//倒数第一位已经比他大了
swap(arr[center],arr[right-1]);
//只需要考虑arr[left+1],arr[right-2]
return arr[right-1];
}
具体算法
public static void quickSort3(int []arr,int left,int right,int CutOff){
int pivot;
int i;
int j;//最右边的位置拿来放pivot
//小于CutOff的值才进行快速排序
if (CutOff<=right-left){
pivot=Median3(arr,left,right);
i=left;
j=right-1;
// boolean flag=true;
while (true){
while (arr[++i]<pivot){}
while (arr[--j]>pivot){}
//退出循环说明左边的大于pivot,右边的小于pivot
//交换这两个元素位置
if (i<j){
swap(arr[i],arr[j]);
}else {
//说明指针交叉了,退出这一个循环
break;
}
}
//把右边的i指针和pivot位置交换,从此pivot到达了最后的位置
swap(arr[i],arr[right-1]);
//由此,一个元素的最终位置确定了,开始左右两边的递归
quickSort3(arr,left,i-1,CutOff);
quickSort3(arr,i+1,right,CutOff);
}else {
insertSort(arr,arr.length);
}
}
附上插入排序
public static void insertSort(int [] arr,int length){
for (int i = 1; i < length ; i++) {
int temp = arr[i];
int j;
for (j = i-1; j > 0 && temp < arr[j]; j--) {
arr[j+1] = arr[j];//前值后移
}
arr[j+1]=temp;
}
}