python数据结构与算法 35 快速排序

快速排序

快速排序也使用了分而治之的策略来提高性能,而且不需要额外的内存,但是这么做的代价就是,列表不是对半切分的,因而,性能上就有所下降。

快速排序选择一个数值,一般称为“轴点”,虽然有很多选取轴点的方法,我们还是简单地把列表中第一个元素做为轴点了。轴点的作用是帮助把列表分为两个部分。列表完成后,轴点所在的位置叫做“切分点”,从这一点上把列表分成两部分供后续调用。

12所示,54将作为轴点。这个例子我们已经排过多次了,我们知道54在排好序后将处于现在31的位置上。找到轴点以后,把列表中其他元素,按比轴点大或比轴点小的方法,分成两部分。


图12  快速排序的第一个轴点

切分开始于定位两个位置标志……就叫做“左标”和“右标”好了……分别位于剩余列表元素的开始和结束处(图131位和8位)。切分的目标,是把相对于轴点来说,位置错误的元素移动在正确的位置上,同时左标和右标也将在“切分点”上相聚。


图13  找到54的切分点

我们把leftmark 不停地+1,直到遇到一个元素值大于轴点。然后把rightmark 不停-1直到遇到一个元素值小于轴点值。这时,我们发现这两个元素的位置不对,如上例中那样,遇到了9320,现在把两个元素交换,然后重复上述过程。

rightmark 小于 leftmark时,停止。这时rightmark 成为切分点,轴点值与切分点的值交换,轴点已经找到位置(图14)。另外,所有切分点左边的元素都小于轴点,所有右边的元素大于轴点,这时列表从切分点处分成两个子表,使用递归调用重复快速排序过程。


图14  完成切分

下面是实现quickSort的函数。它调用了一个递归函数quickSortHelper. quickSortHelper.的基点和归并排序是一样的,如果列表的长度小于等于1,它就是有序的,如果大于1,它就要被切分并且递归排序。Partition 函数完成前面提到过的切分过程。

def quickSort(alist):
   quickSortHelper(alist,0,len(alist)-1)

def quickSortHelper(alist,first,last):
   if first<last:

       splitpoint = partition(alist,first,last)

       quickSortHelper(alist,first,splitpoint-1)
       quickSortHelper(alist,splitpoint+1,last)


def partition(alist,first,last):
   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

alist = [54,26,93,17,77,31,44,55,20]
quickSort(alist)
print(alist)

为了分析快速排序函数性能,要注意到一个长度为n的列表,如果切分点总是在列表的中间,那么就是要logn 次切分。为了找到切分点,n个元素都要与轴点作一次比较,结果需要nlogn.另外,也不需要象归并排序一样消耗额外的内存。不幸的是,在最怀的情况下,切分点不在中间,而是偏左或偏右,生成很不均匀的子表,这种情况下,生成的子表是一个0个元素,一个n-1个元素,然后再切分,却生成一个子表是0个元素,另一个是n-2个元素等等。结果需要的开销就是O(n2)

前面我们提到过有不同的方式来选择轴点,尤其是,我们可以使用一种叫做“三数取中”的技术,以减轻不均匀切分的可能性。这种方法从列表的第一个,中间的一个,最后的一个元素三个数中,取中间值。象上面的例子中,三数是547720,,那么选中54作为轴点。这种思想的来源,是认为第一个元素可能它的大小并不在中间的位置,但是三个数的中间值就比较靠近中间。这种方法特别适合那些开始时已经部分有序的列表。这个轴点值的选择方法留作练习。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值