飞快的递归算法

126 阅读3分钟

这是我参与2022首次更文挑战的第40天,活动详情查看:2022首次更文挑战

飞快的递归算法

递归给我们带来了新的算法实现方式,例如上一章的文件系统遍历。

前几章我们学会了一些排序算法,包括冒泡排序、选择排序和插入排序。但在现实中,数组排序不是通过它们来做的。为了免去大家重复编写排序算法的烦恼,大多数编程语言都自带用于数组排序的函数,其中很多采用的都是快速排序

虽然它已经实现好了,但我们还是想研究一下它的原理,因为其运用递归来给算法提速的做法极具推广意义。

快速排序真的很快。尽管在最坏情况(数组逆序)下它跟插入排序、选择排序的效率差不多,但在日常多见的平均情况中,它的确表现优异。

快速排序依赖于一个名为分区的概念,所以我们先从它开始了解。

分区

此处的分区指的是从数组随机选取一个值,以其为,将比它小的值放到它左边,比它大的值放到它右边。分区的算法实现起来很简单,例子如下所示。

假设有一个下面这样的数组。

从技术上来说,选任意值为轴都可以,我们就以数组最右的值为轴吧。现在轴就是3了,我们把它圈起来。

然后放置指针,它们应该分别指向排除轴元素的数组最左和最右的元素。

接着就可以分区了,步骤如下。

(1)左指针逐个格子向右移动,当遇到大于或等于轴的值时,就停下来。

(2)右指针逐个格子向左移动,当遇到小于或等于轴的值时,就停下来。

(3)将两指针所指的值交换位置。

(4)重复上述步骤,直至两指针重合,或左指针移到右指针的右边。

(5)将轴与左指针所指的值交换位置。

当分区完成时,在轴左侧的那些值肯定比轴要小,在轴右侧的那些值肯定比轴要大。因此,轴的位置也就确定了,虽然其他值的位置还没有完全确定。

让我们来把此流程套到示例数组上。

第1步:拿左指针(正指向0)与轴(值为3)比较。

由于0比轴小,左指针可以右移。

第2步:右移左指针。

将左指针(值为5)与轴比较。它比轴小吗?不。于是左指针停在这里,下一步我们启动右指针。

第3步:比较右指针(值为6)和轴。它比轴大吗?对。于是右指针左移。

第4步:左移右指针。

比较右指针(值为1)和轴。它比轴大吗?不。于是右指针停下。

第5步:因为两个指针都停住了,所以交换它们的值。

随后,再次启动左指针。

第6步:右移左指针。

比较左指针(值为2)和轴。它比轴小吗?对。于是继续右移。

第7步:左指针移到下一格子。注意,这时两个指针都指向同一个值了。

比较左指针和轴。由于左指针的值比轴要大,我们将其停在那里。而且现在左指针与右指针重合,无须再移动指针了。

第8步:到了分区的最后一步,将左指针的值与轴交换位置。

虽然数组还没完全排好序,但我们已完成了一次分区。即比轴(值为3)小的值都聚在了它的左侧,比轴大的值都聚在了它的右侧,这就意味着3已经被放置到正确的位置上了