快速排序 java实现+思路

首先是测试方法

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

猜你喜欢

转载自blog.csdn.net/xushiyu1996818/article/details/81365507