快速排序 Java实现解析

package class03;
/*
    快排的治的逻辑:给定一个数,将数组按小于,等于,大于分成三个区域
    所以会有小于区边界,大于区边界两个变量被定义
 */
public class Code03_PartitionAndQuickSort {
     public static void swap(int[] arr, int i, int j) {
         int temp = arr[i];
         arr[i] = arr[j];
         arr[j] = temp;
     }

     public static int partition(int[] arr, int L, int R){
         if (L > R){
             return -1;
         }
         if (L == R){
             return L;
         }
         int lessEqual = L - 1;
         int index = L;
         while(index < R){
             if(arr[index] <= arr[R]){
                 swap(arr, index, ++lessEqual);
             }
             index++;
         }
         swap(arr, ++lessEqual, R);
         return lessEqual;
     }

     // arr[L...R] 在数组arr范围L到R上使用玩荷兰国旗问题的划分,以arr[R]做划分值
     // 最终返回的是“等于区”左右边界的下标

     public static int[] netherlandsFlag(int[] arr, int L, int R){
         if(L > R){
             return new int[] {-1, -1};
         }
         // 左边界等于右边界时直接返回
         if (L == R){
             return new int[] { L, R};
         }
         int less = L - 1; // less:小于区域右边界 一开始没有包括arr[L]
         int more = R;     // more:大于区域左边界 arr[R]做了划分值,一开始先放在大于区中,在处理过程中不会受到影响
         int index = L; // 从L位置开始遍历
         while (index < more){ // index到达 > 区边界时会停止
             if (arr[index] == arr[R]){
                 // 如果index位置和划分值arr[R]相等,index直接增加
                 index++;
             } else if (arr[index] < arr[R]){
                 /*
                    如果index处的数字比arr[R]小,做三个操作:
                    1.交换arr[index]和小于区边界的下一个数值
                    2.交换数值后,小于区扩容,将刚交换的数值纳入小于区
                    3.index自增1
                 */
                 swap(arr, index++, ++less);
             } else{
                 /*
                    如果index处的数字比arr[R]大,也做三个操作:
                    1.交换arr[index]和大于区边界的左边一个数值
                    2.交换数值后,大于区扩容,将刚交换的数值纳入大于区
                    3.index保持不变,因为刚换过来的数值还没有考察
                 */
                 swap(arr, index, --more);
             }
         }
        /*
            此时L-R被分成了四个区域:
            < 区: L...less
            = 区: less+1...more-1
            > 区: more...R-1
            R: 基准数值 还没动
        */

         /*
            最后一步需要将基准number( arr[R])和>区边界上的数交换,这样就将基准值放进了=区,交换后边界上
            的数到了R位置,仍然在>区内
          */

         /*
            后来区域划分会变为:
            < 区: L...less
            = 区: less+1...more(more位置上时原来的arr[R])
            > 区: more+1...R
          */
         swap(arr, more, R);
         return new int[] {less + 1, more};
     }

     /*
        <=的在左边, 大于的在右边
      */
     public static void quickSort1(int[] arr){
         if(arr == null || arr.length < 2){
             return;
         }
         process1(arr, 0, arr.length - 1);
     }

     public static void process1(int[] arr, int L, int R) {
         if(L >= R){
              return;
         }
         // L...R partition arr[R]
         // [  <=arr[R] | arr[R] | >arr[R] ]
         int M = partition(arr, L, R); // M是最后arr[R]的位置
         process1(arr, L, M - 1);
         process1(arr, M + 1, R);
     }

     // 2.0 利用荷兰国旗问题
    /*
        <的在左边, =的在中间, >的在右边
        和1.0相比优势在于一次可以搞定“等于区”的一批数,这一部分不用继续参与递归
     */
     public static void quickSort2(int[] arr){
         if (arr == null || arr.length < 2){
             return;
         }
         process2(arr, 0, arr.length - 1);
     }

    public static void process2(int[] arr, int L, int R) {
        if(L >= R){
            return;
        }
        int[] equalArea = netherlandsFlag(arr, L, R);
        process2(arr, L, equalArea[0] - 1); // <区
        process2(arr,equalArea[1]+1,R); // >区
    }

    public static void quickSort3(int[] arr){
         if(arr == null || arr.length < 2){
             return;
         }
         process3(arr, 0, arr.length - 1);
    }

    public static void process3(int[] arr, int L, int R){
         if(L >= R){
             return;
         }
         swap(arr, L + (int)(Math.random() * (R - L + 1)), R);
         int[] equalArea = netherlandsFlag(arr, L, R);
         process3(arr, L, equalArea[0] - 1);
         process3(arr, equalArea[1]+1, R);
    }

    // for test
    public static int[] generateRandomArray(int maxSize, int maxValue){
         int[] arr = new int[(int)((maxSize + 1)*Math.random())];
         for(int i = 0; i < arr.length; i++){
             arr[i] = (int)((maxValue + 1)*Math.random()) - (int)(maxValue*Math.random());
         }
         return arr;
    }

    // for test
    public static int[] copyArray(int[] arr){
         if(arr == null){
             return null;
         }
         int[] res = new int[arr.length];
         for(int i = 0;i < arr.length;i++){
             res[i] = arr[i];
         }
         return res;
    }

    // for test
    public static boolean isEqual(int[] arr1, int[] arr2){
         if((arr1 == null && arr2 != null) || (arr1 != null && arr2 == null)){
             return false;
         }
         if(arr1 == null && arr2 == null){
             return true;
         }
         if(arr1.length != arr2.length){
             return false;
         }
        for (int i = 0; i < arr1.length; i++) {
            if (arr1[i] != arr2[i]){
                return false;
            }
        }
        return true;
    }

    // for test
    public static void printArray(int[] arr){
         if(arr == null){
             return;
         }
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + " ");
        }
        System.out.println();
    }

    // for test
    public static void main(String[] args) {
        int testTime = 500000;
        int maxSize = 100;
        int maxValue = 100;
        boolean succeed = true;
        for (int i = 0; i < testTime; i++) {
            int[] arr1 = generateRandomArray(maxSize, maxValue);
            int[] arr2 = copyArray(arr1);
            int[] arr3 = copyArray(arr2);
            quickSort1(arr1);
            quickSort2(arr2);
            quickSort3(arr3);
            if(!isEqual(arr1,arr2) || !isEqual(arr2,arr3)){
                succeed = false;
                break;
            }
        }
        System.out.println(succeed? "Nice!":"Ooops");
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值