Java 快速排序实现

快速排序定义:

快速排序(英语:Quicksort),又称分区交换排序(partition-exchange sort),简称快排,一种排序算法,最早由东尼·霍尔提出。在平均状况下,排序{\displaystyle n}n个项目要{\displaystyle \ O(n\log n)}{\displaystyle \ O(n\log n)}(大O符号)次比较。在最坏状况下则需要{\displaystyle O(n^{2})}{\displaystyle O(n^{2})}次比较,但这种状况并不常见。事实上,快速排序{\displaystyle \Theta (n\log n)}{\displaystyle \Theta (n\log n)}通常明显比其他演算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地达成。

快速排序使用分治法(Divide and conquer)策略来把一个序列(list)分为较小和较大的2个子序列,然后递归地排序两个子序列。

步骤为:

  1. 挑选基准值:从数列中挑出一个元素,称为「基准」(pivot),
  2. 分割:重新排序数列,所有比基准值小的元素摆放在基准前面,所有比基准值大的元素摆在基准后面(与基准值相等的数可以到任何一边)。在这个分割结束之后,对基准值的排序就已经完成,
  3. 递归排序子序列:递归地将小于基准值元素的子序列和大于基准值元素的子序列排序。
    递归到最底部的判断条件是数列的大小是零或一,此时该数列显然已经有序。

选取基准值有数种具体方法,此选取方法对排序的时间性能有决定性影响。

来自 维基 快速排序~

这里进行一下对其的实现:

先说实现思路,在进行上面的步骤2 分割 操作的时候,这个基准的位置是会发生改变的,除非这个基准恰好满足左边的全部小于基准值,右边的全部大于基准值。
然后是怎么实现这个分割呢?在找到一个基准值之后。 比如这里是一个数组,找到某个数为基准值,那么,如何让左边的数全部小于基准值,且右边的全部大于基准值呢?

  • 这里采用的方式是移位与填充的处理方式。比如找到左边的某个数是大于基准值的,那么得进行一个移位的操作。

考虑左右两边的情形:

  • [基准值左边的移动] 比如现在有一个数组是 [1, 7, 0, 3, 4,5, 2], 假设,这里把基准设置为 3 这个值,那么 左边的7 是需要放到 3的后面的,右边的先不考虑;那么移位之后,数组应该变成 [1, 0, 3, 7, 4, 5, 2]了。也就是说,这里是先把 0,3 向左移动,腾出 3 原来的位置,然后把7 插入到3原来的位置上。(这个操作跟插入排序类似)这样一来呢,这个基准值3就左移了一位;
  • [基准值右边的移动] 比如现在有一个数组是 [1, 7, 4, 3, 6, 2, 9],假设,基准值还是这个3,那么右边的这个2是需要移动到基准值前面去的。这时候呢,就把 3,6 右移一位,然后把2 放到原来的3的位置上去,这样之后,数组变成了[1, 7, 4, 2, 3, 6, 9]; 同时这个基准值3的位置就已经发生变化了,被右移了一位。
  • 根据上面这两个方向的移动,可以总结出规律;这样无论基准左右两边有多少个不满足条件的元素,都会被移动到相反的地方去。
  • 根据上面3条,然后就是分别对左边和右边进行递归调用,最终一定是得到一个有序的序列。

代码如下:

    public static void quickSort(int[] array, int left, int right) {
        // 2, 4, 6, 8, 7, 3, 5, 0, 9
        if (SHOW_DETAIL) {
            System.out.println("params:  array = " + Arrays.toString(array) + ", left = " + left + ", right = " + right);
        }
        int pm = (left + right) / 2;
        int pivot = array[pm];
        // System.out.printf("pm=%d, pivot=%d\n", pm, pivot);
        int val = pivot;
        int counts;
        // 处理左边,使左边的全部元素都小于 pivot @start
        // 找到左边 大于 pivot的值, 然后,移动  (arr[t+1],arr[pm]) -> (arr[t],arr[pm-1])  ,并且把  arr[t] -> arr[pm]
        counts = pm - left;
        for (int count = 0; count < counts; ++count) {
            boolean needMove = false;
            for (int i = left; i < pm; ++i) {
                if (needMove) {
                    array[i] = array[i + 1];
                } else {
                    if (array[i] > pivot) {
                        // System.out.println("left===  " + array[i] + ", " + pivot);
                        val = array[i];
                        array[i] = array[i + 1];
                        needMove = true;
                    }
                }
            }
            if (needMove) {
                array[pm] = val;
                pm--;
            }
            // System.out.printf("==left move: %s,pm=%d\n", Arrays.toString(array), pm);
        }
        // // 处理左边,使左边的全部元素都小于 pivot @end

        // 处理右边,使右边的全部元素都大于 pivot @start
        // 处理方式: 找个每个小于 pivot 的值,然后向右移动 (arr[pm],arr[t-1]) -> (arr[pm+1],arr[t])
        counts = right - pm;
        // System.out.printf("right--:pm=%d,right=%d,counts=%d, %s\n", pm, right, counts, Arrays.toString(array));
        for (int count = 0; count < counts; ++count) {
            boolean needMove = false;
            for (int i = right; i > pm; --i) {
                if (needMove) {
                    array[i] = array[i - 1];
                } else {
                    if (array[i] < pivot) {
                        // System.out.println("===--" + array[i] + ", " + pivot);
                        val = array[i];
                        needMove = true;
                        array[i] = array[i - 1];
                    }
                }
            }
            if (needMove) {
                array[pm] = val;
                pm++;
            }
            // System.out.println("right==move: " + Arrays.toString(array));
        }
        // 处理右边,使右边的全部元素都大于 pivot @end
        if (SHOW_DETAIL) {
            System.out.printf("each loop: %s, l=%d,r=%d,pm=%d\n", Arrays.toString(array), left, right, pm);
        }

        // then recursion it
        if (left < pm) {
            // recursion left side
            quickSort(array, left, pm - 1);
        }
        if (pm + 1 < right) {
            // recursion right side
            quickSort(array, pm + 1, right);
        }
    }

注意看上面的代码,会发现这个递归其实没有终止条件。但是实际上不会出现栈溢出的问题。因为每次递归之前会判断输入的参数left, right

调用如下:

 int[] array= {2, -1, 3, -4, 5, -6, 7, -8, 9, 0};
 quickSort(array, 0, array.length - 1);
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值