java实现十大经典排序(六)快速排序

一、什么是快速排序

快速排序(Quicksort)是对冒泡排序的一种改进。

快速排序由C. A. R. Hoare在1960年提出。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。

快速排序算法通过多次比较和交换来实现排序,其排序流程如下:

  1. 首先设定一个分界值,通过该分界值将数组分成左右两部分。
  2. 将大于或等于分界值的数据集中到数组右边,小于分界值的数据集中到数组的左边。此时,左边部分中各元素都小于或等于分界值,而右边部分中各元素都大于或等于分界值。
  3. 然后,左边和右边的数据可以独立排序。对于左侧的数组数据,又可以取一个分界值,将该部分数据分成左右两部分,同样在左边放置较小值,右边放置较大值。右侧的数组数据也可以做类似处理。
  4. 重复上述过程,可以看出,这是一个递归定义。通过递归将左侧部分排好序后,再递归排好右侧部分的顺序。当左、右两个部分各数据排序完成后,整个数组的排序也就完成了。

二、算法稳定性

快速排序是不稳定的,因为会进行元素的交换。
比如数组为 8, 5, 2, 9, 9, 12, 15, 7,交换时第一个9和7发生了交换,导致第一个9排在第二个9后面。

三、算法复杂度

快速排序的一次划分算法从两头交替搜索,直到low和hight重合,因此其时间复杂度是O(n);而整个快速排序算法的时间复杂度与划分的趟数有关。

理想的情况是,每次划分所选择的中间数恰好将当前序列几乎等分,经过log2n趟划分,便可得到长度为1的子表。这样,整个算法的时间复杂度为O(nlog2n)。

最坏的情况是,每次所选的中间数是当前序列中的最大或最小元素,这使得每次划分所得的子表中一个为空表,另一子表的长度为原表的长度-1。这样,长度为n的数据表的快速排序需要经过n趟划分,使得整个排序算法的时间复杂度为O(n2)。

为改善最坏情况下的时间性能,可采用其他方法选取中间数。通常采用“三者值取中”方法,即比较H->r[low].key、H->r[high].key与H->r[(low+high)/2].key,取三者中关键字为中值的元素为中间数。

可以证明,快速排序的平均时间复杂度也是O(nlog2n)。因此,该排序方法被认为是目前最好的一种内部排序方法。

从空间性能上看,尽管快速排序只需要一个元素的辅助空间,但快速排序需要一个栈空间来实现递归。最好的情况下,即快速排序的每一趟排序都将元素序列均匀地分割成长度相近的两个子表,所需栈的最大深度为log2(n+1);但最坏的情况下,栈的最大深度为n。这样,快速排序的空间复杂度为O(log2n)。

四、java实现快速排序

  1. 设置两个变量i、j,排序开始的时候:i=0,j=N-1;
  2. 以第一个数组元素作为关键数据,赋值给key,即key=A[0];
  3. 从j开始向前搜索,即由后开始向前搜索(j–),找到第一个小于key的值A[j],将A[j]和A[i]的值交换;
  4. 从i开始向后搜索,即由前开始向后搜索(i++),找到第一个大于key的A[i],将A[i]和A[j]的值交换;
  5. 重复第3、4步,直到i == j; 3,4步中,没找到符合条件的值,即3中A[j]不小于key,4中A[i]不大于key的时候改变j、i的值,使得j=j-1,i=i+1,直至找到为止。找到符合条件的值,进行交换的时候i, j指针位置不变。另外,i==j这一过程一定正好是i+或j-完成的时候,此时令循环结束。
	private static int swapNum = 0;

    public static void main(String[] args) {
        int[] array = {3, 1, 2, 4, 9, 8, 7, 5, 6};
        quickSort(array);
        System.out.println("交换次数:" + swapNum);
        System.out.println("排序后的数组:" + JSON.toJSONString(array));
    }

    public static void quickSort(int[] sourceArray) {

        // 首先判断数组长度是否小于2, 小于2直接返回
        if (sourceArray == null || sourceArray.length < 2) {
            return;
        }

        sort(sourceArray, 0, sourceArray.length - 1);
    }

    private static void sort(int[] arr, int l, int r) {
        if (l < r) {
            // 寻找调整后的基准数位置
            int pivot = arr[l];
            int left = l;
            int right = r;
            while (left < right) {
                while (left < right && arr[right] >= pivot) {
                    right--;
                }
                if (left < right) {
                    swapNum++;
                    arr[left++] = arr[right];
                }

                while (left < right && arr[left] < pivot) {
                    left++;
                }
                if (left < right) {
                    swapNum++;
                    arr[right--] = arr[left];
                }
            }
            arr[left] = pivot;
            // 左右分治
            sort(arr, l, left - 1);
            sort(arr, left + 1, r);
        }
    }

控制台打印信息如下:

交换次数:5
排序后的数组:[1,2,3,4,5,6,7,8,9]

详细讲解,可以参考:
白话经典算法系列之六 快速排序 快速搞定

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值