算法回顾——快速排序

快速排序(quickSort),一种典型的运用分治思想的算法。
核心思想是:

  1. 在数组中取一个元素作为关键的中间值keyVal。
  2. 然后把比中间值小的元素,放到中间值的左边;把比中间值大的元素,放到中间值右边。
  3. 在中间值keyVal的左半边和右半边两个子列表中,重复上面的操作。把一个数组的排序,拆分成多个规模更小的问题,逐一解决,分而治之,这就是分治了;

语言描述,我们很好懂。但是怎么转换成代码实现出来呢?这就是快速排序算法的关键,采用头尾两个下标往中间逼近的做法。如图所示:
![![在这里插入图片描述](https://img-blog.csdnimg.cn/20200311181238360.pg](https://img-blog.csdnimg.cn/20200311181548240.png
主要流程如下:
a. 取第一个元素7作为中间值(取数组中的任意元素都可以);
b. 先从右边开始,下标递减,找到比中间值7小的元素,放到中间值左边;
c. 然后从左边开始,下标递增,找到比中间值7大的元素,放到中间值右边;
d. 重复b、c两步,在左右下标 l 和 r相遇的时候,就走完一遍,左边元素都比中间值小,右边都比中间值大了。

以上图的数组为例,结合图示看一下具体过程:

  • 取数组第一个元素的值7作为中间值;
  • 执行b步骤,先 r 下标递减,找到第一个比7小的元素3。

按上面快排思路我们应该把它放到7的左边,但是数组长度固定,没法像人的思维那样直观的放到7左边啊,怎么办呢?首先我们可能想到开辟额外的空间来存储,但是在考虑尽量节省空间的前提下,我们可以采用原地交换中间值7和当前访问值3的方法,3就到7左边了:
在这里插入图片描述

  • 执行c步骤,l 下标递增,找到第一个比7大的元素10,按上面的方法原地交换:
    ![在这里插入图片描述](https://img-blog.csdnimg.cn/20200312093512637.png
    ![在这里插入图片描述](https://img-blog.csdnimg.cn/20200312093935610.png
  • 重复b步骤,r 下标递减,找到第一个比7小的元素1,原地交换:
    ![在这里插入图片描述](https://img-blog.csdnimg.cn/20200312094619864.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0NoaW5hX1N0eWxl,size_16,color_FFFFFF,t_70
  • 重复c步骤,l 下标递增,找到第一个比7大的元素9,原地交换:
    在这里插入图片描述
  • 重复b步骤,r下标递减,发现l和r下标相遇了(l=r=6),结束操作。
    在这里插入图片描述
    如上图最后的数组状态所示,现在已经左边的元素都比中间值7小、右边的元素都比中间值7大了。我们注意到:虽然最终中间值7在index=6的位置,但是在中间过程中,它的位置是有多次变动的,

接下来,对中间值7左边的子序列和右边的子序列分别重复上述的过程,就把对整个数组的排序分成了两个规模更小的子任务,在子序列中又会分成两个更小的子序列执行相同操作,以此类推,直到子序列只有一个或两个元素,完成交换后,因为所有局部子序列都是有序的,且左边子序列都比右边小,所以数组整体也是有序的,排序完成。

快速排序Java实现:

private static void quickSort(int[] arr, int start, int end) {
    if (start >= end) {
        return;
    }
    int keyVal = arr[start];
    int left = start;
    int right = end;
    while (left < right) {
        while (left < right && arr[right] >= keyVal) {
            right--;
        }
        if (left < right) {
            swap(arr, left, right);
        }
        while (left < right && arr[left] < keyVal) {
            left++;
        }
        if (left < right) {
            swap(arr, left, right);
        }
    }
    // 通过递归调用,在比中间值小的子序列和比中间值大的子序列中,进行相同处理
    quickSort(arr, start , left - 1);
    quickSort(arr, left + 1, end);
}
// 交换元素
private static void swap(int[] arr, int index1, int index2) {
    int tmp = arr[index1];
    arr[index1] = arr[index2];
    arr[index2] = tmp;
}

插个题外话,在练习的时候,可以用洗牌算法,来生成一个乱序的数组:

private int[] genArray(int len) {
    int[] arr = new int[len];
    //初始化数组
    for (int i = 0; i < len; i++) {
        arr[i] = i + 1;
    }
    Random r = new Random(System.currentTimeMillis());
    // 打乱数组元素顺序。经典的洗牌算法,保证每个元素出现在每个位置的概率相等。
    for (int i = len - 1; i > 0; i--) {
        int index = r.nextInt(i + 1);
        int iVal = arr[i];
        arr[i] = arr[index];
        arr[index] = iVal;
    }
    System.out.println(Arrays.toString(arr));

    return arr;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值