快排

挑选一个元素作为标点,把数组划分为小于和大于的区域;
需注意标点的选取(如果取第0个,在有序情况下会退化,栈溢出);
相等元素处理(如果全部相等,算法退化,栈溢出,二路快排,三路快排)

关键点:看partition函数写法,起始点,循环不变量及结束点

快排

partition函数维护小于arr[l]部分,起始点在l(可能都大于arr[l]),结束部分做交换后,返回即为等于部分。

import java.util.Random;

public class QuickSort {

    public static > void sort(E[] arr) {

        sort(arr,0,arr.length-1,new Random()); // 闭区间
    }

    public static > void sort(E[] arr, int L, int r, Random random) {
        if (L>=r) {
            return;
        }

        int jIndex = partition(arr,L,r,random);
        sort(arr,L,jIndex-1,random);
        sort(arr,jIndex+1,r,random);
    }

    public  static > int partition(E[] arr, int L, int r ,Random random) {
        /**
         *  有序的数组,
         *  分割左右部分时,只能区分首节点和大于的部分,会一直递归下去,导致栈溢出,
         *  时间复杂度为O(n²),空间复杂度使用堆栈n次调研堆栈
         *
         *  使用随机值解决index值解决该问题
         *  (虽然解决了有序数组问题,但遇到全部相等的数组又会退化)
         */
        int p = L + random.nextInt(r-L+1);
        swap(arr,p,L);

        // 循环不变量 arr[L+1...j]v
        int j = L; // 注意j的初始化位置
        for (int i = L+1; i <= r; i++) {
            //arr[i]小于arr[L],j++
            //大于j的位置不变,不操作
            if (arr[i].compareTo(arr[L])<0) {
                j++;
                swap(arr,i,j); // j 处的位置小于arr[L]
            }
        }
        swap(arr,L,j);
        return j;
    }

    private static  void swap(E[] arr,int i,int j){
        E t = arr[i];
        arr[i] = arr[j];
        arr[j] = t;
    }
}

2路快排:解决元素全部相等的算法退化

循环不变量(双路实现,解决全部相等问题,等于部分平均分在两边,位置定位在中间)
注意partition函数
1、初始化: int i = L+1 , j=r ,
2、循环体中,先循环找出左边小于arr[l]和右边大于arr[l],循环结束的地方即为左边大右边小,需要交换,注意交换后对值进行了++和--,导致最后右边指针指向小于l处的值
3、结束时,j指向元素<=arr[l],左后和j交换

import java.util.Random;

/**
 * 双路快速排序(主要加入等于的判断,平均分布在小于和大于区间)
 */
public class QuickSort2Ways {

    public static > void sort(E[] arr) {

        sort(arr,0,arr.length-1,new Random()); // 闭区间
    }

    public static > void sort(E[] arr, int L, int r, Random random) {
        if (L>=r) {
            return;
        }

        int jIndex = partition(arr,L,r,random);
        sort(arr,L,jIndex-1,random);
        sort(arr,jIndex+1,r,random);
    }

    public  static > int partition(E[] arr, int L, int r ,Random random) {
        /**
         *  有序的数组,
         *  分割左右部分时,只能区分首节点和大于的部分,会一直递归下去,导致栈溢出,
         *  时间复杂度为O(n²),空间复杂度使用堆栈n次调研堆栈
         *
         *  使用随机值解决index值解决该问题
         *  (虽然解决了有序数组问题,但遇到全部相等的数组又会退化)
         */
        int p = L + random.nextInt(r-L+1);
        swap(arr,p,L);

        // 循环不变量(双路实现,解决全部相等问题,位置定位在中间) arr[L+1...j]<=v ; arr[j+1..i]>=v
        int i = L+1 , j=r; // 注意j的初始化位置
        while (true) {

            while (i<=j && arr[i].compareTo(arr[L])<0) { // arr[L+1...j]<=v 如果遇到大于arr[L],终止循环
                i++;
            }

            while (j>=i && arr[j].compareTo(arr[L])>0) { //arr[j+1..i]>=v 如果遇到小于arr[L],终止循环
                j--;
            }

            if (i>=j) { // i>j 超过大于和小于范围, i==j 证明该处等于arr[L]
                break;
            }

            // i处为大于arr[L],j为小于arr[L],交换位置,符合区间范围
            swap(arr,i,j);
            i++;
            j--;
        }
        swap(arr,L,j);
        return j;
    }

    private static  void swap(E[] arr,int i,int j){
        E t = arr[i];
        arr[i] = arr[j];
        arr[j] = t;
    }
}

3路快排

把区域划分为小于、等于、大于三部分
partition函数:
1、初始位置int lt = L, i= L+1 , gt=r+1,lt和gt在范围之外(全部大于或小于),
2、过程中[l+1...lt]arr[l],等于部分不操作,隐含为(lt...i]==arr[l]
3、结尾lt和l交换后,[lt...gt)==arr[l]

import java.util.Random;

/**
 * 三路快速排序(partition中将数组分成三段,小于,等于,大于)
 */
public class QuickSort3Ways {

    public static > void sort(E[] arr) {

        sort(arr,0,arr.length-1,new Random()); // 闭区间
    }

    public static > void sort(E[] arr, int L, int r, Random random) {
        if (L>=r) {
            return;
        }

        int[] jIndex = partition(arr,L,r,random);
        sort(arr,L,jIndex[0]-1,random);
        sort(arr,jIndex[1],r,random);
    }

    public  static > int[] partition(E[] arr, int L, int r ,Random random) {
        /**
         *  有序的数组,
         *  分割左右部分时,只能区分首节点和大于的部分,会一直递归下去,导致栈溢出,
         *  时间复杂度为O(n²),空间复杂度使用堆栈n次调研堆栈
         *
         *  使用随机值解决index值解决该问题
         *  (虽然解决了有序数组问题,但遇到全部相等的数组又会退化)
         */
        int p = L + random.nextInt(r-L+1);
        swap(arr,p,L);

        // 循环不变量(三路实现,解决全部相等问题,区间分成三段)
        // arr[L+1...lt-1]v
        int lt = L, i= L+1 , gt=r+1; // 初始区间为空,lt和gt都没有指向区域第一个(参考循环不变量范围)
        while (i 0) {
                gt--;
                swap(arr,i,gt); // 交换位置后,i处新值还未做比较,所以不i++
            } else { // 相等arr[L],不做处理,因为lt+1到i-1区间都等于arr[L]
                i++;
            }
        }
        swap(arr,L,lt);
        return new int[]{lt,gt};
    }

    private static  void swap(E[] arr,int i,int j){
        E t = arr[i];
        arr[i] = arr[j];
        arr[j] = t;
    }
}

你可能感兴趣的:(快排)