坐在马桶上看算法:快速排序
今天,我们将看一个非常重要的排序算法: quicksort 。 Quicksort是一种采用分而治之策略的递归排序算法。
我不会在这里解释递归的工作原理,因为我已经在这里写了一篇有关递归的文章。
由于这是一种分而治之的算法,我们希望获取未排序整数的列表,然后将问题分解为两个更简单的问题,然后将每个问题分解……。 等等。
为此,我将首先介绍quicksorts的核心操作:分区。 其工作方式如下:
>>> A = [6, 3, 17, 11, 4, 44, 76, 23, 12, 30]
>>> partition(A, 0, len(A)-1)
>>> print(A)
[6, 3, 17, 11, 4, 23, 12, 30, 76, 44]
那么,这里发生了什么以及它如何工作? 我们需要选择一些数字作为我们的枢纽 。 我们的分区函数接受3个参数, 列表 , 列表中的第一个元素和数据透视表 。 我们要在此处实现的目标是,在对列表进行分区时,枢轴左侧的所有内容都小于枢轴 ,而右侧列表中的所有内容都大于枢轴 。 对于上面看到的第一个分区, 30是我们的枢纽 。 分区后,我们看到一些元素的位置发生了变化,但30左边的所有元素都小于它,而右边的所有元素都大于了它。
那对我们意味着什么? 好,这意味着30现在在列表中的正确位置,我们现在有两个更容易名单排序。 所有这些都是就地完成的,因此我们不会创建新列表。
让我们看一下代码:
def partition(A, p, r):
q = j = p
while j < r:
if A[j] <= A[r]:
A[q], A[j] = A[j], A[q]
q += 1
j += 1
A[q], A[r] = A[r], A[q]
return q
最后的返回q对于我们的分区不是必需的,但对整个列表进行排序是必不可少的。 上面的代码遍历列表A,并维护索引p,q,j,r 。
p是固定的,是列表中的第一个元素。 r是枢轴,并且是列表中的最后一个元素。 已知A [p:q-1]范围内的元素小于或等于轴,并且A [q-1:r-1]中的所有值都大于轴。 唯一改变的索引是q和j 。 在每一步中,我们将A [j]与A [r]进行比较 。 如果它大于枢轴,则它处于正确的位置,因此我们增加j并移至下一个元素。 如果A [j]小于A [r],我们将A [q]与A [j]交换。 交换之后,我们增加q ,从而扩展了已知小于或等于枢轴的元素范围。 我们还将j递增以移至下一个要处理的元素。
现在进入快速排序部分。 请记住,这是一种递归算法,因此它将连续调用partition(),直到没有剩余的分区为止。
让我们看一下代码:
def quicksort(A, p, r):
if r <= p:
return
q = partition(A, p, r)
quicksort(A, p, q-1)
quicksort(A, q+1, r)
return A
就这么简单。 我们在这里所做的只是检查枢轴的索引是否小于或等于我们要分区的列表开头的索引。 如果是,则返回,因为传递的任何列表都不需要进一步分区。
否则,我们对列表A进行分区,然后在两个新的子列表上再次调用quicksort 。
Quicksort在完全混乱的大型列表上效果最佳。 在几乎排序的列表上,它的性能确实很差。 或以Big-O表示法,最好的情况(加扰)是O(n log(n)),最坏的情况下(几乎或完全排序的列表)是O(n ^ 2)。
我们的新书“将Slither转换成Python”中对此主题有更详细的介绍,您现在只需5.99欧元即可预订—我们为初学者编写的Python编程语言入门,旨在使您从一个完整的初学者到熟练的人。和熟练的程序员,仅22章,涵盖了整个主题。 在这里查看。
翻译自: https://hackernoon.com/algorithms-explained-quicksort-324305b8757b
坐在马桶上看算法:快速排序