快排

/**
 * 快排概念:
 * 快排也属于交换排序,通过元素之间的比较和交换位置来达到排序的目的;在每一轮挑选一个基准元素,并让其他比它大的元素移动到数列一边,
 * 比它小的元素移动到数组的另一边,从而将数组拆成两个部分,这种思路叫做分治法
 * <p>
 * 时间复杂度:
 * 原数组在分支法的思想下,每一轮都会被拆分为两部分,所以遍历的轮数为logn,每一轮的比较和交换需要把数组全部遍历一遍,
 * 这个时间复杂度是O(n),所以总的平均时间复杂度是O(nlogn)
 * 但是如果将原本逆序的数列排序成顺序数列,整个数列并没有被分成两半,每一轮都只确定来基准元素的位置,数列的第一个元素要么是最大值要么是最小值,
 * 无法发挥分支法的优势,这个时候快速排序需要进行n轮,时间复杂度退化为O(n平方)。
 * 解决思路是:随机选择一个元素作为基准元素
 */
public class QuickSort {

    /**
     * 递归的方式
     *
     * @param arr
     * @param startIndex
     * @param endIndex
     */
    public static void quickSort(int[] arr, int startIndex, int endIndex) {
        if (arr == null || startIndex >= endIndex) {
            return;
        }
        //得到基准元素位置
        int pivotIndex = partition(arr, startIndex, endIndex);
        //根据基准元素位置分成两部分进行递归
        quickSort(arr, startIndex, pivotIndex - 1);
        quickSort(arr, pivotIndex + 1, endIndex);
    }

    /**
     * 循环方式
     * 引入一个存储Map类型元素的栈,用于存储每一次交换时的起始下标和结束下标
     * 每一次循环,都会让栈顶元素出栈,通过partition方法方法进行分支,并且按照基准元素的位置分成左右两部分,左右两部分再分别入栈。
     * 当栈为空时,说明排序已经完毕,推出循环
     */
    public static void quickSort2(int[] arr, int startIndex, int endIndex) {
        Stack<Map<String, Integer>> stack = new Stack<>();
        Map<String, Integer> root = new HashMap<>();
        root.put("startIndex", startIndex);
        root.put("endIndex", endIndex);
        stack.push(root);
        while (!stack.isEmpty()) {
            Map<String, Integer> item = stack.pop();
            int pivotIndex = partition(arr, item.get("startIndex"), item.get("endIndex"));
            if (item.get("startIndex") < pivotIndex - 1) {
                Map<String, Integer> left = new HashMap<>();
                left.put("startIndex", item.get("startIndex"));
                left.put("endIndex", pivotIndex - 1);
                stack.push(left);
            }
            if (pivotIndex + 1 < item.get("endIndex")) {
                Map<String, Integer> right = new HashMap<>();
                right.put("startIndex", pivotIndex + 1);
                right.put("endIndex", item.get("endIndex"));
                stack.push(right);
            }
        }
    }

    /**
     * 选择基准元素位置:(单边循环法)
     * 首先选定基准元素 pivot,同时设置一个mark指针指向数列起始位置,这个mark指针代表小于基准元素的区域边界
     * 接下来从基准元素的下一个位置开始遍历数组
     * 如果遍历到的元素大于基准元素,则继续往下遍历
     * 如果遍历到的元素小于基准元素,需要做两件事情。
     * 第一:将mark元素向右移动1位,因为小于pivot基准元素的区域边界增大了1
     * 第二:将遍历到的最新的元素和mark位置指针所在的元素互换位置,因为最新遍历的元素归属于小于pivot的区域
     *
     * @param arr
     * @param startIndex
     * @param endIndex
     * @return
     */
    public static int partition(int[] arr, int startIndex, int endIndex) {
        int pivot = arr[startIndex];
        int mark = startIndex;
        for (int i = startIndex + 1; i <= endIndex; i++) {
            if (arr[i] < pivot) {
                mark++;
                int temp = arr[mark];
                arr[mark] = arr[i];
                arr[i] = temp;
            }
        }
        arr[startIndex] = arr[mark];
        arr[mark] = pivot;
        return mark;
    }

    public static void main(String[] args) {
        int[] arr = {4, 4, 6, 5, 3, 2, 8, 1};
        quickSort2(arr, 0, arr.length - 1);
        System.out.println(Arrays.toString(arr));

    }

}

 

发布了28 篇原创文章 · 获赞 5 · 访问量 4867
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 书香水墨 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览