首先是测试方法
package algorithm.sort.quickSort;
import algorithm.sort.quickSort.Solution;
public class main {
public static void main(String[] args) {
//int[][] testTable = {{1,2,3,0},{1,2,3,4},{1,2567678,3,4,6767,45,12,345,3435,34,66666},
//{1,1,1,3,3,4,3,2,4,2},{1,6,2,7,1}};
int[][] testTable={createRandomIntArray(-100,100,50),createRandomIntArray(-200,200,100),createRandomIntArray(-1000,1000,500)};
for (int[] ito : testTable) {
test(ito);
}
}
private static void test(int[] ito) {
boolean rtn;
long begin = System.currentTimeMillis();
for (int i = 0; i < ito.length; i++) {
System.out.print(ito[i]+" ");
}
System.out.println();
//开始时打印数组
Solution.quickSort(ito,0,ito.length-1);//执行程序
long end = System.currentTimeMillis();
//System.out.println(ito + ": rtn=" +rtn);
for (int i = 0; i < ito.length; i++) {
System.out.print(ito[i]+" ");
}//打印结果几数组
System.out.println();
System.out.println("耗时:" + (end - begin) + "ms");
System.out.println("-------------------");
System.out.println("-------------------");
}
public static int[] createRandomIntArray(int min,int max,int length){
int[] result=new int[length];
for(int i=0;i<length;i++){
double rand=Math.random();
result[i]=(int)(min+(max-min)*rand);
}
return result;
}
}
然后是java的实现代码及思路
package algorithm.sort.quickSort;
import java.util.Arrays;
/* 快速排序
1、先从数列中取出一个数作为基准数
2、分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边
3、再对左右区间重复第二步,直到各区间只有一个数
下面一段是别人的思路
1.i =L; j = R; 将基准数挖出形成第一个坑a[i]。
2.j–由后向前找比它小的数,找到后挖出此数填前一个坑a[i]中。
3.i++由前向后找比它大的数,找到后也挖出此数填到前一个坑a[j]中。
4.再重复执行2,3二步,直到i==j,将基准数填入a[i]中。
*/
public class Solution {
public static void quickSort(int[] nums,int begin,int last) {
int length=nums.length;
if(length==0||length==1){
return;
}
int base=nums[begin];
int i=begin;
int j=last;
int mid=0;
//i为第一个,j为最后一个,base为第一个数字,此时i处为base
while(true){
//j从后往前找到比base小的数字,与i作颠倒,颠倒后j处为base,i处为之前j处比base小的数
while(j!=i){
if(nums[j]<base ){
nums[i]=nums[j];
nums[j]=base;
break;
}
else{
j--;
}
}
//如果上一个j过程结束,如果i与j相同,中间为i,跳出大过程
if(i==j){
mid=i;
break;
}
//i从后往前找到比base大的数字,与j作颠倒,颠倒后i处为base,j处为之前i处比base大的数,跳出这个小循环
while(i!=j){
if(nums[i]>base){
nums[j]=nums[i];
nums[i]=base;
break;
}
else{
i++;
}
}
//如果上一个i过程结束,如果i与j相同,中间为i,跳出大过程
if(i==j){
mid=i;
break;
}
}
//System.out.println(Arrays.toString(nums));
//从此处开始mid处为base,比mid处大的,则比base大,反之亦然
//对两次进行递归快速排序
if((mid-begin)>1){
quickSort(nums, begin, mid-1);
}
if((last-mid)>1){
quickSort(nums, mid+1, last);
}
return;
}
}
快速排序算法复杂度分析
①快速排序的时间复杂度与枢轴元素的选取息息相关。平均情况下,时间复杂度为O(NlogN),最坏情况下为O(N^2)
②枢轴元素的选取有多种方式:比如上面提到的三数取中,也可以采用随机化算法来选取。采用三数取中时,几乎不会出现最坏的情况。相对基于内存排序的其他算法,快速的效率是很高的,它的时间复杂度的常数因子很小。大约是1.3,而归并大约是1.4 —-不太确定。 1.3NlogN
③快排用到了递归,当数组元素个数很少时,递归的开销就有点大了,故在程序中可将快排与插入排序结合起来。
下面是别人的
https://www.2cto.com/kf/201604/498227.html
1、快速排序的思路
快速排序和归并排序的思路很相似,都是采取的分治思想。快速排序通过选择一个元素,该元素称为枢轴元素或切分元素,然后将它放到一个合适的位置上,使得它前面的元素不大于它,它后面的元素不小于它,然后将枢轴元素为分界点,两边的数组也采取类似的方法,即选取枢轴元素,使得前面的元素不大于它,后面的不小于它,重复进行下去,直到数组里面只有一个元素(递归退出条件)。
2、partition函数
从上述的描述来看,快速排序是需要递归的,递归地选取枢轴元素进行切分。所以,快速排序的实现重点是切分(partition)函数,即如何实现对于某一切分元素,使得它前面的元素不大于它,后面的不小于它。
3.1 partition函数实现之一
《Algorithm Fourth Edition》上的思路:对于某一枢轴元素,从第一元素开始往后扫描,找到第一个大于它的元素,然后从最后一个元素往前扫描,找到第一个小于它的元素,交换两个元素。要注意扫描不能出现数组访问越界,且扫描开始位置不能相交。
3.2 partition函数实现之二
选择第一个元素为枢轴元素,使用index为当前扫描元素的pointer,storIndex表示枢轴元素后面最后一个小于枢轴元素的pointer,从枢轴元素后面的第一个元素开始从左往右扫描,若当前扫描的元素比枢轴元素小,则交换index与++storIndex的元素(即第一个不小于枢轴元素的元素),进行一趟扫描后,将枢轴元素与storIndex的元素相交换,以将枢轴元素放到合适的位置。
点击这里选择QUICK即可查看动态执行情况。
3.3 partition函数实现之三
先将枢轴元素临时保存起来,从右往左扫描,找到第一个小于枢轴元素的元素,将其放到枢轴元素的位置,然后从左往右扫描,找到第一个大于枢轴元素的元素,将它放到之前第一个小于枢轴元素的位置。
对比三种实现,第二种最不好,相比第一种实现方式,同样一趟排序,平均情况下其交换次数第二种要比第一种至少多一倍;对比第一种和第三种实现方式,第三种访问数组的次数要少些,因为第一种采取的是交换,第一种很好理解,实现起来也容易,第三种代码紧凑些,理解稍微难那么点。
4、快速排序的改进
4.1 改进枢轴元素的选取
最好情况下,枢轴元素应该是所有元素的平均值,即中值,这样就更接近归并排序的切分情况。但是前面的三种partition实现都是选取的第一个元素为枢轴元素,并不能有这个保证,采取三数中值法(三取样切分),比较lo,mid,hi的大小,选取中间的一个作为枢轴元素。
其实还可以5取样切分,那样会更接近中数,但是过于繁琐。
4.2 切换到插入排序
对于小规模数组,插入排序够用了,用快速排序多次切分访问数组的次数将比插入排序多些,还不如用插入排序,故数组规模较小时,切换到插入排序。
最后附上改进后的快速排序Java实现
优化 别人的
https://blog.csdn.net/scgaliguodong123_/article/details/46473365
https://blog.csdn.net/insistgogo/article/details/7785038