快速排序
快速排序使用分而治之来获得与归并排序相同的优点,而不使用额外的存储。
算法步骤:
首先选择一个值,该值称为 枢轴值 。使用列表中的第一项作为枢轴值。枢轴值的作用是帮助拆分列表。枢轴值属于最终排序列表(通常称为拆分点) 的实际位置,将用于将列表划分为快速排序的后续调用。如果列表的长度小于或等于一,它已经排序。 如果它更大,那么它可以被分区和递归排序。
分区从通过在列表中剩余项目的开始和结束处定位两个位置标记(我们称为左标记和右标记) 开始。分区的目标是移动相对于枢轴值位于错误侧的项,同时也收敛于分裂点。
首先增加左标记(左标记右移),直到左标记指向的值大于枢轴值。 然后递减右标(右标记左移),直到右标记指的值小于枢轴值。这两个值就是相对于最终分裂点位置(指的就是枢轴值)不适当的项。 交换这两个项,然后重复该过程。
在右标变得小于左标记的点时停止。右标记的位置现在是分割点。枢轴值可以与拆分点的内容交换,枢轴值现在就位。 此外,分割点左侧的所有项都小于枢轴值,分割点右侧的所有项都大于枢轴值。
在分割点处划分列表,并且可以在两半上递归调用快速排序。
完整代码:
def quickSort(alist):
"""
快排的主要函数
:param alist: 要排序的列表
:return:
"""
quickSortHepler(alist, 0, len(alist) - 1)
def quickSortHepler(alist, first, last):
"""
快排使用的递归函数
:param alist: 要排序的列表
:param first: 首位作为左标记
:param last: 末位作为右标记
:return:
"""
if first < last:
# 对列表分区
splitpoint = partition(alist, first, last)
# 递归调用,对两个半块上进行快排
quickSortHepler(alist, first, splitpoint - 1)
quickSortHepler(alist, splitpoint + 1, last)
def partition(alist, first, last):
"""
分区函数
:param alist: 要排序的列表
:param first: 首位
:param last: 末位
:return:
"""
# 首位作为枢轴值
pivotvalue = alist[first]
# 确定左标记和右标记
leftmark = first + 1
rightmark = last
done = False
while not done:
# 增加左标记(左标记右移),直到左标记指向的值大于枢轴值
while leftmark <= rightmark and alist[leftmark] <= pivotvalue:
leftmark = leftmark + 1
# 递减右标(右标记左移),直到右标记指的值小于枢轴值
while alist[rightmark] >= pivotvalue and rightmark >= leftmark:
rightmark = rightmark - 1
# 右标变得小于左标记的点时,停止循环
if rightmark < leftmark:
done = True
else:
# 交换左标记与右标记指向的值
temp = alist[leftmark]
alist[leftmark] = alist[rightmark]
alist[rightmark] = temp
# 交换枢轴值和分割点(右标记)的值
temp = alist[first]
alist[first] = alist[rightmark]
alist[rightmark] = temp
return rightmark
进阶
通过使用称为中值三的技术来减轻一些不均匀分割的可能性。要选择枢轴值,我们将考虑列表中的第一个,中间和最后一个元素。想法是,在列表中的第一项不属于列表的中间的情况下,中值三将选择更好的“中间”值。当原始列表部分有序时,这将特别有用。