挑战408——数据结构(7)——快速排序算法

本章前面介绍的合并排序算法在理论上表现良好,也具有O(N log N)的最差情况复杂度,实际上并没有太多的应用。 相反,目前使用的大多数排序程序都是基于由英国计算机科学家C.A.R.(Tony)Hoare开发的称为Quicksort的算法.。

快速排序原理

Quicksort和合并排序都采用分治法。在合并排序算法中,原始vector被分为两部分,每一个被独立排序。 然后将所得到的排序向量合并在一起以完成整个向量的排序操作。合并排序是根据N的总个数进行对半分割的,假设我们改变分割的方式,我们在分割的时候,将较小的元素分在开头,将较大的元素分到末尾,结果又将如何?我们来看一个实例:
在这里插入图片描述
我们可以直接观察到,这些元素中的一半大于50并且一半小于50,因此在这种情况下我们可以将“small”的定义为小于50的数,将 “large”定义为大于50的数。

如果可以找到一种重新排列元素的方式,以便所有的 “small”元素都在开头,所有的“large”元素都在最后,那么可能的排序方式有以下两种:

  • 当以这下图种方式将元素划分为多个部分时,我们只需要对某个指定的数进行排序(比如下图,比50小的放一边,比50大的放一边)。 由于边界线左侧的所有元素都小于右边的所有元素,因此最终结果将是完全排序的vector:
    在这里插入图片描述
  • 如果你运气好,,每次都可以随时选择每一轮的小和大元素之间的最优边界(即中位数),则该算法将每次将向量划分一半,最终与合并排序具有相同的定性特征。然而在实践中,Quicksort算法选择向量中的一些现有元素,并将该值用作小元素和大元素之间的分界线。例如,一个常见的方法是选择第一个元素,它是原始vector中的第1个元素(56),并将其作为小元素和大元素之间的分界点。 当向量重新排序时,边界因此处于特定的下标位置而不是两个位置之间,如下所示:
    在这里插入图片描述
    从这一点来看,递归调用必须对位置0和3之间vector元素进行排序,和将位置5和7之间的vector元素进行排序,将下标位置4留在它原来的位置。
    正如合并排序中一样,Quicksort算法的simple case是大小为0或1的vector,因为已当vector只有一个或者没有元素的时候,他就是被排好序的了。
快速排序算法

Quicksort算法的递归部分包括以下步骤:

  • . 选择一个元素作为小元素和大元素之间的边界(Choose an element to serve as the boundary between the small and large elements.)。这个元素被称为枢轴(pivot)。 目前为此选择任何元素都是可行的,最简单的方法是选择向量中的第一个元素。
  • . 分割vector(partitioning the vector).。重新排列vector中的元素,使大的元素朝向vector的末端移动,使小的元素朝向vector的头部移动。(Rearrange the elements in the vector so that large elements are moved toward the end of the vector and small elements toward the beginning) 这一步骤的目的是将边界位置附近的元素分开,使边界左边的所有元素都小于枢轴,右边的所有元素大于或等于枢轴。 这个处理称为分割vector(partitioning the vector).
  • 对每个部分vector中的元素进行排序(Sort the elements in each of the partial vectors)。 因为枢轴边界左边的所有元素都严格小于右边的所有元素,所以对每个vector进行排序时必须以排序的顺序保留整个vector(即不能开辟额外的存储空间来存放排好序的vector)。此外,由于该算法使用了分治策略,所以可以使用Quicksort的递归应用对这些较小的向量进行排序。

这里的难点在于第二步,分割vector。我们将分割后的部分分为三个类:小于枢轴的元素, 枢轴它本身,与枢轴至少一样大的元素。由于不用开辟新的vector用于储存排好的元素,这一步我们通常可以通过交换元素来实现

假设我们在分割之前,就选好了vector的第一个元素为枢纽,这样我们可以立即判断一个值是相对于该轴的大还是小。Tony Hoare的分割算法图解如下:

  1. 暂时忽略索引位置0的pivot元素(即枢纽),并集中在其余的元素上。使用两个下标值lh和rh来记录vector其余部分中第一个和最后一个元素的下标位置,如下所示:
    在这里插入图片描述
  2. 将rh下标向左移动,直到与lh重合,或指向相对于枢轴较小的值的元素。在这个例子中,位置7中的值30已经是一个很小的值(相对于56来说),所以rh下标不需要移动。
  3. 将lh下标向右移动,直到与rh重合,或指向包含大于或等于枢轴的值的元素。在此示例中,lh下标必须向右移动,直到它指向大于56的元素,于是有下图的结果:
    在这里插入图片描述
  4. 如果lh和rh并未指向相同的位置,则在vector中交换那些位置的元素,使其看起来像这样:
    在这里插入图片描述
  5. 重复步骤2到4,直到lh和rh位置重合。例如,在下一遍,在步骤4中的交换操作交换19和95.一旦发生这种情况,则下一步执行步骤2将rh下标移动到左侧,其结束匹配lh,如下
    在这里插入图片描述
  6. 除非所选的枢轴恰好恰好是整个vector中的最小元素(代码中包含对这种情况的特殊检查),否则lh和rh索引位置重合的点将是在vector中最右边的最小值。剩下的步骤是在vector的开始处使用pivot元素在该位置交换该值,如下所示:
    在这里插入图片描述

这样, 枢轴值位于标记的边界位置,左侧的每个元素都比枢纽较小,右侧每个元素至少都大于等于枢纽。

下一篇,将结合C++代码分析快速排序的算法复杂度。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值