排序算法之快速排序

介绍

通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列

 

也就是随便选择一个数字,然后根据这个数字进行拆分数组。和合并排序有些像,合并排序也是计算出一个下标,而合并排序是从中间拆开。

快速排序类似,选择一个序列中的数字,比这个数字大的放到左边,比这个数字小的则放在右边。和这个数字相等的放在中间,然后在合并三个序列,左+中+右。递归进行就可以得到有序数组。

代码实现:

依然是新建个方法

public static int[] quickSort(int[] A){}

因为是递归所以首先终止条件

if(A.length<=1){
            return A;
        }

选择一个数组,并声明记录左、中、右小标和数组。

// 选择一个数字
        int selectNum = A[0];
        // 记录左边下标
        int leftIndex=0;
        // 记录右边边下标
        int rightIndex=0;
        // 记录中间下标
        int midIndex=0;
        // 声明三个数组
        int[] leftArray =new int[A.length];
        int[] rightArray =new int[A.length];
        int[] midArray =new int[A.length];

进行遍历原数组,拆分。

// 进行循环对比,如果大于的则放左边,小于的则放右边,不大于不小于的则放中间。
        for (int i = 0; i < A.length; i++) {
            if(A[i]>selectNum){
                rightArray[rightIndex++]=A[i];
            }else if(A[i]<selectNum){
                leftArray[leftIndex++]=A[i];
            }else{
                midArray[midIndex++]=A[i];
            }
        }

因为之前声明的数组是总数组的长度。所以数组有溢出,进行数组的截取。如果没有储存数据则把数组置空,因为声明的时候数组中有多个0,左右分别递归快速排序

// 如果左边数组有,则进行截取,因为声明数组的时候是全数组长度,有溢出。截取多余的0
        if(leftIndex>0){
            leftArray = Arrays.copyOfRange(leftArray,0,leftIndex);
            // 递归快速排序
            leftArray = quickSort(leftArray);
        }else{
            // 如果没有进行存储数组,则把数组置空,因为声明的时候数组里面有多个000000
            leftArray = new int[]{};
        }
        if(rightIndex>0){
            rightArray = Arrays.copyOfRange(rightArray,0,rightIndex);
            rightArray = quickSort(rightArray);
        }else{
            rightArray = new int[]{};
        }
        if(midIndex>0){
            midArray = Arrays.copyOfRange(midArray,0,midIndex);
        }else{
            midArray = new int[]{};
        }

合并数组:

// 声明数组用于储存分好的三个数组。
        int[] B = new int[leftArray.length+midArray.length+rightArray.length];
        // 依次把左、中、右存放到数组中
        System.arraycopy(leftArray,0,B,0,leftArray.length);
        System.arraycopy(midArray,0,B,leftArray.length,midArray.length);
        System.arraycopy(rightArray,0,B,leftArray.length+midArray.length,rightArray.length);

然后返回该数组,

 

完整代码:

public static int[] quickSort(int[] A){
        if(A.length<=1){
            return A;
        }
        // 选择一个数字
        int selectNum = A[0];
        // 记录左边下标
        int leftIndex=0;
        // 记录右边边下标
        int rightIndex=0;
        // 记录中间下标
        int midIndex=0;
        // 声明三个数组
        int[] leftArray =new int[A.length];
        int[] rightArray =new int[A.length];
        int[] midArray =new int[A.length];
        // 进行循环对比,如果大于的则放左边,小于的则放右边,不大于不小于的则放中间。
        for (int i = 0; i < A.length; i++) {
            if(A[i]>selectNum){
                rightArray[rightIndex++]=A[i];
            }else if(A[i]<selectNum){
                leftArray[leftIndex++]=A[i];
            }else{
                midArray[midIndex++]=A[i];
            }
        }
        // 如果左边数组有,则进行截取,因为声明数组的时候是全数组长度,有溢出。截取多余的0
        if(leftIndex>0){
            leftArray = Arrays.copyOfRange(leftArray,0,leftIndex);
            // 递归快速排序
            leftArray = quickSort(leftArray);
        }else{
            // 如果没有进行存储数组,则把数组置空,因为声明的时候数组里面有多个000000
            leftArray = new int[]{};
        }
        if(rightIndex>0){
            rightArray = Arrays.copyOfRange(rightArray,0,rightIndex);
            rightArray = quickSort(rightArray);
        }else{
            rightArray = new int[]{};
        }
        if(midIndex>0){
            midArray = Arrays.copyOfRange(midArray,0,midIndex);
        }else{
            midArray = new int[]{};
        }
        // 声明数组用于储存分好的三个数组。
        int[] B = new int[leftArray.length+midArray.length+rightArray.length];
        // 依次把左、中、右存放到数组中
        System.arraycopy(leftArray,0,B,0,leftArray.length);
        System.arraycopy(midArray,0,B,leftArray.length,midArray.length);
        System.arraycopy(rightArray,0,B,leftArray.length+midArray.length,rightArray.length);
        return  B;
    }

 

List进行快速排序

其实数组可以用List代替,使用java8新特性代码会变得简单很多。如下:

public static List<Integer> quickSort(List<Integer> A){
        if(A.size()<=0){
            return A;
        }
        // 选择一个数字
        Integer select = A.get(0);
        // 获取小于选择数字的集合
        List<Integer> leftList = A.stream().filter(x->x<select).collect(Collectors.toList());
        // 获取大于选择数字的集合
        List<Integer> rightList = A.stream().filter(x->x>select).collect(Collectors.toList());
        // 获取等于选择数字的集合
        List<Integer> minList = A.stream().filter(x-> x.equals(select)).collect(Collectors.toList());
        // 左右进行重新排序
        leftList=quickSort(leftList);
        rightList=quickSort(rightList);
        // 合并集合
        List<Integer> resultList = new ArrayList<>();
        resultList.addAll(leftList);
        resultList.addAll(minList);
        resultList.addAll(rightList);
        return resultList;
    }

逻辑大概与上面代码一样。

 

 

网上有个快速排序的动态图,

 

 

在一个课程上面又看到另一中快速排序,但是有局限性。选择元素必须是序列的最左边的元素。

代码如下:

public void sort(int[] A) {
        this.quickSort(A, 0, A.length);
    }

    private void quickSort(int[] A, int l, int r) {

        if(r - l <= 1) {
            return;
        }
        // 拆分序列,左边大于选择的,右边小于选择的,得到的i就是截取的下标
        int i = partition(A, l, r);
        // 拆分后的元素进行重新快排
        quickSort(A, l, i);
        quickSort(A, i+1, r);

    }

    private int partition(int[] A, int l, int r) {
        // 选取元素
        int x = A[l];

        // 序列类似是这种 |X|left----|right--|
        // l+1也就是从第二个元素,越过x开始比较
        int i = l + 1;
        // 元素的终止下标
        int j = r;
        while(i != j) {
            // 如果当前遍历元素小于x,则跳过,因为是从左往右开始遍历所以小于x的就在右边。i++ 下一个遍历
            if(A[i] < x) {
                i++;
            } else {
                // 如果当前遍历元素大于x,则和最后一个元素换位置,
                // 也就是把大于选择元素的那个放在最后面。y--也就是最后一个已经排序好了不进行遍历排序。
                swap(A, i, --j);
            }
        }
        swap(A, i-1, l);
        return i-1;

    }

    private void swap(int[] A,int x,int y){
        int tmp = A[x];
        A[x] = A[y];
        A[y] = tmp;
    }

 

 

 依次类推进行递归。

举个例子,如果选的是23的话,23和5对比,则5小于23,i++然后进行下个循环,如下所示:

 

posted @ 2021-06-06 09:25  苦心明  阅读(217)  评论(0)    收藏  举报