__author__ = 'steven'
# coding=utf-8
'''快速排序
它采用了一种分治的策略,通常称其为分治法(Divide-and-ConquerMethod)。
该方法的基本思想是:
1. 从数列中挑出一个元素,称为 “基准”(pivot).
2. 分区操作(partition):重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。
在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
3. 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
快排的特点:
元素的移动效率高了,尽量让一次移动,为后面的操作服务.
——高效的排序算法对元素的移动效率都是比较高的。
时间复杂度:
第一个关键字正好是待排序的序列的中间值(比如1到8中的5),因此递归树是平衡的,
此时性能也比较好。此时时间复杂度是 O(nlogn)
在最坏状况下则需要Ο(n^2)次比较(如原始是咧为倒序倒序),此时的递归树是一棵严重偏斜的树。
稳定性:
由于关键字的比较和交换是跳跃进行的,因此,快速排序是一种不稳定的排序方法。
参考:http://book.51cto.com/art/201108/287089.htm
'''
list = [5, 8, 1, 4, 2, 7, 3, 6]
# list = [8, 7, 6, 5, 4, 3, 2, 1]
def quick_sort(list, left, right):
if left >= right: # 当前分区排序的停止条件,返回的数列为 整个list(而不是分区中的部分数列的,但是发生变化的是分区中的部分数列)
return list # 当前分区间内操作完成后,小于基数的数都在基数左边,大于基数的数都在基数右边
start = left # start 记录当前排序分区的开始位置
end = right # end 记录当前分区排序的停止位置
key = list[start] # 每次分区开始后,以第一个数为比较的基数
# key = list[(start + end)//2]
while left < right:
# 右指针向前移动,直到找到比基数大的数,则跳出循环,并将该数赋值给左指针所指位置————
# 此时,右指针相当于在这个位置挖了个坑,随时等着左指针找一个小于基数的数来填上这个本来比基数大的数的位置.
while left < right and list[right] >= key:
right -= 1
list[left] = list[right]
# 左指针向后移动,直到找到一个大于基数的数,则跳出循环,并赋值给当前右指针所停留的位置————
# 即补上上次右指针在右边挖的坑,但赋值的同时,相当于左指针给自己当前位置又挖了个坑,再次等右指针的大数来填这个坑
while left < right and list[left] <= key:
left += 1
list[right] = list[left]
# 在左右相互填坑的游戏中,如果左右指针重合了——表示左指针左边的数都比基数小,右指针右边的数都比基数大
list[right] = key # 则最终这个坑由基数来填上,此时本轮填坑游戏结束,并在进去下个分区排序的流程前,通过 return返回list.
quick_sort(list, start, left - 1)
quick_sort(list, left + 1, end)
return list
print('排序结果为:')
print(quick_sort(list, 0, len(list) - 1))