一、快速排序(quick sort)
快速排序,又称划分交换排序(partition-exchange sort),通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
排序步骤为:
1、从数列中挑出一个元素,称为"基准"(pivot)(初始一般为第一个元素)
2、重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区结束之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
3、递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
快速排序代码实现:
# -*- encoding: utf-8 -*-
"""
@File : quick_sort.py
@Time : 2019/11/21 9:53
@Author : chen
"""
# 快速排序
def quick_sort(li, start, end):
# low = 0
# high = len(li) - 1 # 队列最后元素的下标 需要-1
low = start
high = end
mid = li[low] # 排序基准初始设定
# if start >= end: # 递归的退出条件 通过low、high改变,再递归修改了start和end
if low >= high: # 递归的退出条件 两种条件相同意思
return
while low != high: # 也可以使用low < high判断条件 low==high的时候循环退出
while low < high and li[high] >= mid: # 当游标high所指的元素值 >= 基准值,不做交换,high的游标往前移动
high -= 1
# if li[high] < mid: # 当游标high所指的元素值 < 基准值,将high所指的值赋值给low游标
li[low] = li[high]
while low < high and li[low] < mid: # 当游标low所指的元素值 < 基准值,不做交换,low 的游标往前移动
low += 1
# if li[low] > mid: # 当游标low所指的元素值 > 基准值,将low所指的值赋值给high游标
li[high] = li[low]
li[low] = mid # low==high的时候循环退出执行这句,此时的low和high已经循环完整个列表,处于同一位置,此位置就是mid应该放入的位置
# 对前面的元素进行快速排序,比mid小的部分
quick_sort(li, start, low-1)
# 对后面的元素进行快速排序,比mid大的部分
quick_sort(li, low+1, end)
if __name__ == '__main__':
li = [3, 4, 57, 32, 13, 46, 21, 55, 77]
quick_sort(li, 0, len(li)-1)
print(li)
快速排序不是稳定的排序算法,不能保证相同元素的顺序不变。它的最坏情况时间复杂度是O(n^2).
二、归并排序
归并排序的思想就是先递归分解数组,再合并数组。
将数组分解最小之后,然后合并两个有序数组,基本思路是比较两个数组的最前面的数,谁小就先取谁,取了后相应的指针就往后移一位。然后再比较,直至一个数组为空,最后把另一个数组的剩余部分复制过来即可。
# -*- encoding: utf-8 -*-
"""
@File : merge_sort.py
@Time : 2019/11/21 17:28
@Author : chen
"""
'''
归并排序
将数组分解最小之后,然后合并两个有序数组,基本思路是比较两个数组的最前面的数,谁小就先取谁,取了后相应的指针就往后移一位。
然后再比较,直至一个数组为空,最后把另一个数组的剩余部分复制过来即可。
'''
# 归并排序
def merge_sort(li):
'''
:param li:
:return: 排序列表
'''
# 停止拆分条件 拆成单独的一个元素
if len(li) <= 1:
return li
# 二分分解
mid_index = len(li) // 2
left_li = merge_sort(li[:mid_index]) # 被拆分的数列的左边部分
right_li = merge_sort(li[mid_index:]) # 被拆分的数列的左边部分 注意:的位置
left_index = 0 # 左边数列游标
right_index = 0 # 右边数列游标
result = [] # 把排序好的添加进这个list
# 空list的情况
while left_index < len(left_li) and right_index < len(right_li): # 循环条件:左右游标要小于被拆分的数列长度
if left_li[left_index] < right_li[right_index]: # 比较游标所指的两个元素大小
result.append(left_li[left_index]) # 添加进新的list
left_index += 1 # 左游标继续移动
else:
result.append(right_li[right_index]) # 右游标的值最小
right_index += 1 # 右游标继续移动
print("左边拆分数列:", left_li) # 左边排序的list
print("右边拆分数列:", right_li) # 右边排序的list
print("排序数列:", result)
result += left_li[left_index:] # 当游标遍历完拆分的list,合并左右列表
result += right_li[right_index:]
return result # 排序完成的列表
if __name__ == '__main__':
li = [3, 4, 57, 32, 13, 46, 21, 55, 77]
m = merge_sort(li)
print("最后排序完成列表:", m)
三、常见排序算法效率比较:
递归、冒泡、选择排序
辅助空间:辅助空间是完成算法所需的额外空间,O(1)是最理想的,这样的算法其辅助空间不依赖于问题的规模,换句话说这样的算法是稳定的。同时也有O(n),O(logn)~O(n)