Python3实现快速排序
这里是对几年前用python2快排的一些改进。这里仍然交代一下算法的基本思想和实现步骤。最后谈下可优化的一些方法。
#基本思想
通过一趟排序将要排序的数据分割成独立的两个部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按次方法对这两部分数据分别进行快速排序,整个排序过程递归进行,以此来达到整个数据列变成有序数列。
步骤如下:
1.设置两个变量i和j,令序列第一个元素为基准元素(pivot)。
2.i指向序列最左,j指向序列最右,j从右往左试探,i从左往右试探,直到j找到小于基准的数就停止,i找到大于基准的数就停止,交换i和j指向的两个数。并且j继续往左,i继续往右直至i与j相遇。
3.当i和j相遇,则i或j上的元素与基准元素(pivot)交换,完成一轮排序。
4.此时基准元素(pivot)左边均是比基准元素(pivot)小的数,基准元素(pivot)右边均是比基准元素(pivot)大的数,再以递归的方式对左右两边的数组重复以上三步过程的比较。
#代码实现
python版本:3.7
代码执行环境:win10+pycharm
# 快速排序入口
def quickly_sort(list):
sort_list(list, 0, len(list)-1)
# 快速排序进行分段排序,该函数在原有序列的基础上不新开辟内存空间直接对序列做操作。
def sort_list(list, first, last):
if first < last:
split = partition(list, first, last)
sort_list(list, first, split - 1)
sort_list(list, split + 1, last)
def partition(list, first, last):
# list取得是当前分割后的列表,first为当前分割后列表的首部,last取得是当前分割后列表的尾部
pivot = list[first]
first_mark = first
last_mark = last
while True:
# 当队尾游标找到比pivot小的数的时候结束循环,并交由队首游标循环找出比pivot大的数进行交换
while list[last_mark] > pivot:
if last_mark == first_mark:
break
last_mark -= 1
# 当对首游标找到比pivot大的数的时候结束循环,为了防止最后一次队首队尾下标越界,需要判断队首队尾游标位置。
while list[first_mark] <= pivot:
# 当队首游标与队尾游标重合之后立即终止跳出循环防止越界
if first_mark == last_mark:
break
else:
first_mark += 1
# 当队首游标与队尾游标重合之后,将pivot与左右重合游标进行替换。
if first_mark == last_mark:
list[first], list[first_mark] = list[first_mark], list[first]
break
# 当队首队尾游标没有重合,则将左右游标数据进行交换
else:
list[last_mark], list[first_mark] = list[first_mark], list[last_mark]
# 返回下一个pivot的序列值
return first_mark
a = []
for i in range(1, 100):
a.append(random.randint(1, 100))
print(a)
quickly_sort(a)
print(a)
结果:
输入
[19, 100, 7, 33, 50, 42, 39, 9, 20, 88, 16, 61, 99, 29, 57, 8, 62, 59, 51, 14, 20, 59, 62, 40, 17, 23, 94, 96, 57, 57, 97, 52, 2, 79, 70, 20, 68, 46, 5, 39, 54, 78, 9, 10, 21, 64, 69, 87, 64, 47, 13, 21, 57, 66, 90, 57, 47, 46, 18, 55, 87, 34, 22, 6, 1, 56, 19, 43, 75, 82, 68, 43, 75, 60, 65, 81, 5, 14, 82, 74, 77, 66, 60, 25, 14, 54, 13, 6, 39, 33, 54, 81, 33, 3, 35, 67, 59, 14, 84]
输出
[1, 2, 3, 5, 5, 6, 6, 7, 8, 9, 9, 10, 13, 13, 14, 14, 14, 14, 16, 17, 18, 19, 19, 20, 20, 20, 21, 21, 22, 23, 25, 29, 33, 33, 33, 34, 35, 39, 39, 39, 40, 42, 43, 43, 46, 46, 47, 47, 50, 51, 52, 54, 54, 54, 55, 56, 57, 57, 57, 57, 57, 59, 59, 59, 60, 60, 61, 62, 62, 64, 64, 65, 66, 66, 67, 68, 68, 69, 70, 74, 75, 75, 77, 78, 79, 81, 81, 82, 82, 84, 87, 87, 88, 90, 94, 96, 97, 99, 100]
#优化的一些借鉴想法
1.优化基准元素的选择
快速排序算法的快慢取决于基准元素的选择。上面的例子中,选择的是待排序数组中的第一个元素。事实上,固定选择第一个元素作为基准元素是不合理的。试想如果基准元素在进行一次交换操作后,整个序列没有发生实质性的操作。最坏情况下,待排序的数组是正序或逆序时,每次划分只得到一个比上次少一个元素的子序列,另一个子序列为空。此时快速排序的时间复杂度就变成了0(n**2)。一般选择基准元素的方法是:三数取中法。即取三个元素(三个元素分别是待排数组的左端点、中间端点、右端点)先进行从小到大的排序,然后选择中间端点的元素作为基准元素。当待排序数组是非常大时,采用九数取中法,即从数组中分三次采样,每次取三个数,三个数各取出中间的元素,总共得到三个中间元素。接着,从这三个中间元素中再取一个中间数作为基准元素。
2.优化不必要的交换
增加一个哨兵位置,暂存基准元素的值。然后每次当小于基准元素的元素出现在右边或大于基准元素的元素出现在左边时,进行直接替换操作,不进行交换,