Hello,大家好,今天继续排序系列之六讲☞《快速排序法进阶》,之所以称为进阶,那肯定是因为比vision 1.0高明。为了方便大家比较,首先同大家一起回顾一下第一版快速排序的原理。

        首先拿序列的最后一项作为基准,其余项依次和他做比较,凡是比他小的都通过交换函数swap()依次和list[0],list[1]...进行交换,最后基准项自己和最后一个比他小的项的后面一项交换位置。所以经过这一轮循环,最终比基准项小的都被放在了序列的最前面,比基准项大的都被放在了序列的后面。这样原序列就以基准项为界被分为两个小序列。然后对每个小序列做同样处理,直至最小的序列仅含一个元素为止。

      今天小编和大家分享一个更高明的快速排序,它通过“精准交换”使得交换次数更少,进而提高排序效率!为了更好的方便大家理解,我们通过一个例子来说明。

        假定初始序列为“6 1 2 7 9 3 4 5 10 8”, 基准值为序列最左端的数6。首先分别从两端开始寻找“精准交换的对象”。先从右往左找一个小于 6 的数,再从左往右找一个大于 6 的数,然后交换它们。这里可以用两个变量 i 和 j,分别指向序列最左边和最右边。刚开始的时候让 i 指向序列的最左边(即 i=0),指向数字 6。让 j 指向序列的最右边(即 j=9),指向数字 8。

        首先 j 开始出动。因为此处设置的基准数是最左边的数,所以需要让哨兵 j 先出动,这一点非常重要(请自己想一想为什么)。 j 一步一步地向左挪动,直到找到一个小于 6 的数停下来。接下来 i 再一步一步向右挪动(即 i++),直到找到一个大于 6的数停下来。最后 j 停在了数字 5 面前,i 停在了数字 7 面前。现在交换 i 和 j 所指向的元素的值。交换之后的序列6 1 2 5 9 3 4 7 10 8 。到此第一轮“探测”真正结束。此时以基准数 6 为分界点,6 左边的数都小于等于 6,6右边的数都大于等于 6。回顾一下刚才的过程,其实 j 的使命就是要找小于基准数的数,而 i 的使命就是要找大于基准数的数,直到 i 和 j 碰头为止。

      OK,解释完毕。现在基准数 6 已经归位,它正好处在序列的第 6 位。此时我们已经将原来的序列,以 6 为分界点拆分成了两个序列,左边的序列是“3 1 2 5 4”,右边的序列是“9 7 10 8”。接下来还需要分别处理这两个序列,因为 6 左边和右边的序列目前都还是很混乱的。不过不要紧,我们已经掌握了方法,接下来只要递归刚才方法分别处理 6 左边和右边的序列即可。

        废话不多说,直接上代码。

Part one
Part two

   

Code Test

      快速排序之所以比较快,是因为相比冒泡排序,每次交换是跳跃式的。每次排序的时候设置一个基准点,将小于等于基准点的数全部放到基准点的左边,将大于等于基准点的数全部放到基准点的右边。这样在每次交换的时候就不会像冒泡排序一样只能在相邻的数之间进行交换,交换的距离就大得多了。因此总的比较和交换次数就少了,速度自然就提高了。当然在最坏的情况下,仍可能是相邻的两个数进行了交换。因此快速排序的最差时间复杂度和冒泡排序是一样的,都是 O(N2),它的平均时间复杂度为 O (NlogN)。其实快速排序是基于一种叫做“二分”的思想。