ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

分区函数和快排(快排分为递归和非递归两个版本)

2022-08-24 23:30:29  阅读:152  来源: 互联网

标签:index arr 递归 索引 int 分区 快排 等于 public


package class08;

import java.util.Arrays;
import java.util.Stack;

/**
 * 分区函数和快排。
 * 快排分为递归和非递归两个版本。
 */
public class Code03_PartitionAndQuickSort {
    /**
     * 分区。结果是分为两个区域,小于等于区(不保证有序),和大于区(不保证有序)。
     * 以数组arr的最后一个数为基准数,将小于等于它的数,归为小于等于区。大于它的数,归于大于区。
     *
     * @param arr 需要被分区的数组
     */
    public static void splitNum1(int[] arr) {
        //分区函数的默认的前提条件是,数组的最后一个数为基准数。(即arr[N - 1]为基准数)
        int lessEqualsR = -1;//小于等于基准数,区域的最右位置的索引。
        int index = 0;//当前数的索引。即当前要判断的数,判断它是小于等于基准数,还是大于基准数。
        int N = arr.length;
//        while (index < N - 1) {//这个是不对的。
        //index是索引,N是数组的个数,所以,当index=6时,N等于7时,就是数组的最后一个数,和自己比较大小。
        //那么(arr[index] <= arr[N - 1])的结果是true。所以会有一个交换的操作。把基准数换到它该在的位置。
        while (index < N) {//只要当前数的索引(指针),小于数组长度。
            if (arr[index] <= arr[N - 1]) {//1.如果当前数小于等于基准数。
                swap(arr, ++lessEqualsR, index++);//将小于等于区的再右边一个数,和当前数,交换。指针index往后跳一步,即index++。
                //上边这一句顶下边这三句。
                //swap(arr, lessEqualsR + 1, index);//将小于等于区的再右边一个数,和当前数,交换。
                //lessEqualsR++;//小于等于区,右扩一个位置。
                //index++;//指针index跳一步。
            } else {//2.如果当前数大于基准数。
                index++;//指针index直接++。不做交换操作,小于等于区,也不右扩。
            }
            printArr(arr);
        }
    }

    /**
     * 分区。结果是分为三个区域,小于区(不保证有序),等于区(都是等于基准数的,所有可以归为有序),和大于区(不保证有序)。
     *
     * @param arr 需要被分区的数组
     */
    public static void splitNum2(int[] arr) {
        int lessR = -1;//小于区的最右位置的索引。
        int N = arr.length;
        int moreL = N - 1;//大于区的最左位置的索引。
        int index = 0;//当前数的索引。将要对当前数做判断,判断它是小于基准数,还是等于,还是大于。
        while (index < moreL) {//只要当前数的索引,小于大于区的最右位置的索引。
            if (arr[index] < arr[N - 1]) {//1.如果当前数小于基准数。
                swap(arr, ++lessR, index++);//小于区的再右边一个数,和当前数,交换。(小于区右扩一个位置)。当前数跳一步(index++)。
            } else if (arr[index] > arr[N - 1]) {//2.如果当前数大于基准数。
                //大于区的再左边一个数,和当前数,交换。(大于区左扩一个位置)。当前数保持位置不变。
                //因为交换后的当前数,是一个新数,还没判断呢。所有index不能往后跳一步。
                swap(arr, --moreL, index);
            } else {//3.当前数大于基准数。
                index++;//index直接跳一步。不做交换操作。小于区不右扩,大于区也不左扩。
            }
        }
        //跳出循环后,记得:交换数组的最后一个数和大于区的最左位置的数。
        //因为最后一个数就是基准数本身,并不大于基准数。它只是一直"潜伏"在大于区内部。
        //所以在调整完小于区,等于区,和大于区后,最后记得将最后一个数,放在它该在的位置,即等于区的最后位。也就是moreL索引位置。
        swap(arr, N - 1, moreL);
    }

    //part2
    //快排(1.递归版本)
    public static void quickSort1(int[] arr) {
        if (arr == null || arr.length <= 1) {
            return;
        }
        process(arr, 0, arr.length - 1);
    }

    //使arr在L到R上,有序。
    public static void process(int[] arr, int L, int R) {
        if (L >= R) {
            return;
        }
        int[] equalsArea = partition(arr, L, R);
        process(arr, L, equalsArea[0] - 1);//equalsArea[0]:等于区域的第一个数的索引。
        process(arr, equalsArea[1] + 1, R);//equalsArea[1]:等于区域的最后一个数的索引。
    }

    /**
     * 在arr[L...R]范围上,将arr[R]作为基准数。划分出小于区,等于区,和大于区。
     *
     * @param arr 要被分区的数组
     * @param L   arr的最左索引
     * @param R   arr的最右索引
     * @return 一个只有两个元素的数组。
     * 第一个数代表:等于区域的第一个数的索引。
     * 第二个数代表:等于区域的最后一个数的索引。
     */
    public static int[] partition(int[] arr, int L, int R) {
        int lessR = L - 1;//当L等于0时,lessR等于-1,所以抽象后后,lessR等于L-1。
        int moreL = R;//当R等于N-1时,moreL等于N-1,所以抽象化后,moreL等于R。
        int index = L;//当L等于0时,index等于0,所以抽象化后,index等于L。
        while (index < moreL) {//当R等于N-1时,index<N-1,所以抽象化后,index<moreL。
            if (arr[index] < arr[R]) {//原来是:arr[index] < arr[N-1]。基准数的索引,永远是[L...R]范围上的R。
                swap(arr, ++lessR, index++);
            } else if (arr[index] > arr[R]) {//原来是:arr[index] > arr[N-1]。基准数的索引,永远是[L...R]范围上的R。
                swap(arr, --moreL, index);
            } else {
                index++;
            }
        }
        swap(arr, moreL, R);//记得将[L...R]范围上,最后索引位置的数,放在它该在的位置上。也就是归位。
        //等于区域的最左数的索引是,小于区域的最右数的索引,再+1。
        //等于区域的最右数的索引是,大于区域的最左数的索引。没有-1。因为arr[N-1]和arr[moreL]交换了,所以moreL就是等于区域的最右数的索引。
        return new int[]{lessR + 1, moreL};
    }

    //part3
    //2.快排(非递归版本)
    public static void quickSort2(int[] arr) {
        if (arr == null || arr.length <= 1) {
            return;
        }
        Stack<Job> stack = new Stack<>();//定义一个栈,用来存放任务。
        stack.push(new Job(0, arr.length - 1));//先往任务栈里添加一个任务:在[0,N-1]上排序。
        while (!stack.empty()) {//只要任务栈不是空的,就继续。
            Job cur = stack.pop();//弹出一个任务,记为当前任务。
            int[] equalsArea = partition(arr, cur.L, cur.R);//将当前任务的左边界,和右边界,作为参数,调用分区函数partition()。返回一个等于区。
            if (equalsArea[0] > cur.L) {//当等于区的第一个元素,大于当前任务的左边界,才有小于区域。
                stack.push(new Job(cur.L, equalsArea[0] - 1));//向任务栈中添加一个任务,范围是,当前任务的左边界,和等于区的第一个数的索引的,前一个位置。
            }
            if (equalsArea[1] < cur.R) {//当等于区的第二个元素,小于当前任务的有边界,才有大于区域。
                stack.push(new Job(equalsArea[1] + 1, cur.R));//向任务栈中添加一个任务,范围是,等于区的最后一个数的索引的,后一个位置,和当前任务的右边界。
            }
        }
    }

    /**
     * 自定义任务类,在[L,R]山排序。
     * 比如:最大的任务就是将数组arr,在0到arr.length-1范围上,排序。
     * new Job(0, arr.length - 1);
     * 属性L:当前任务的左边界
     * 属性R:当前任务的右边界
     */
    public static class Job {
        public int L;
        public int R;

        public Job(int left, int right) {
            L = left;
            R = right;
        }
    }

    //交换数组arr中i和j索引位置的两个数
    public static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

    //打印数组
    public static void printArr(int[] arr) {
        for (int num : arr) {
            System.out.print(num + " ");
        }
        System.out.println();
    }

    //生成随机数组
    public static int[] generateRandomArr(int maxValue, int maxSize) {
        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;
    }

    //复制数组
    public static int[] copyArr(int[] arr) {
        int[] arr2 = new int[arr.length];
        for (int i = 0; i < arr.length; i++) {
            arr2[i] = arr[i];
        }
        return arr2;
    }

    //判断两个数组是否相等
    public static boolean isEquals(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;
    }

    public static void main(String[] args) {
//        int[] arr = {5, 1, 3, 2, 4, 6, 5, 8, 7, 9, 3, 5};
//        printArr(arr);
//        splitNum1(arr);
////        splitNum2(arr);
//        printArr(arr);

        int testTimes = 100000;
        int maxValue = 10000;
        int maxSize = 100;
        boolean flag = true;
        System.out.println("test start!");
        for (int i = 0; i < testTimes; i++) {
            int[] arr0 = generateRandomArr(maxValue, maxSize);
            int[] arr1 = copyArr(arr0);
            int[] arr2 = copyArr(arr0);
            quickSort1(arr1);
            quickSort2(arr2);//快排,非递归。
            Arrays.sort(arr0);
            if (!isEquals(arr0, arr1) || !isEquals(arr0, arr2)) {
                System.out.println("Oops1!");
                flag = false;
                break;
            }
        }
        System.out.println("test end!");
        System.out.println(flag ? "Nice!" : "Oops2!");
    }
}

 

标签:index,arr,递归,索引,int,分区,快排,等于,public
来源: https://www.cnblogs.com/TheFloorIsNotTooHot/p/16622674.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有