Python经典排序算法
这个文章很nice
这个动图优秀
https://www.icourse163.org/course/zju-93001 mooc浙大 数据结构 陈越、何钦铭
冒泡排序、选择排序、插入排序这三个是最慢也是最经典的三个排序算法
快速排序对于大的乱数串列一般相信是最快的已知排序
冒泡排序 bubble sort
最简单的排序算法,也是效率最差的,因为它必须在最终位置知道前交换,浪费许多“交换操作”
如果列表已经排序,则是最好情况,遍历期间无需交换,如果发现已经排序,可以提前终止,这种修改下的冒泡通常称为短冒泡排序
时间复杂度: 平均o(n^2) 最差o(n^2) 最好o(n)
空间复杂度:o(1)
稳定
#测试输入数组
alist = [27, 33, 28, 4, 2, 26, 13, 35, 8, 14]
#冒泡排序
for i in range(len(alist)-1):
for j in range(len(alist)-1-i):
if alist[j] > alist[j+1]:
alist[j],alist[j+1] = alist[j+1],alist[j]
#排序结果输出
print('sorted:',alist)
选择排序 selection sort
选择排序改进了冒泡排序,每次遍历列表只做一次交换
时间复杂度:平均o(n^2) 最差o(n^2) 最好o(n^2)
空间复杂度:o(1)
不稳定
#测试输入数组
alist = [27, 33, 28, 4, 2, 26, 13, 35, 8, 14]
#选择排序
for i in range(len(alist)-1):
least = i
for j in range(i+1,len(alist)):
if alist[j] < alist[least]:
least = j
if least != i:
alist[least],alist[i] = alist[i],alist[least]
#排序结果输出
print('sorted:',alist)
插入排序 insertion sort
它始终在列表的较低位置维护一个排序的子列表,然后将每个新项 “插入” 回先前的子列表,使得排序的子列表成为较大的一个项
时间复杂度: 平均o(n^2) 最差o(n^2) 最好o(n)
空间复杂度:o(1)
稳定
#测试输入数组
alist = [27, 33, 28, 4, 2, 26, 13, 35, 8, 14]
#插入排序
for i in range(1,len(alist)):
j = i;
while j>0 and alist[j-1]>alist[j]:
alist[j-1],alist[j] = alist[j],alist[j-1]
j -= 1
#排序结果输出
print('sorted:',alist)
希尔排序 shell sort
是1959年shell发明,第一个突破o(n^2)的排序,是对简单插入排序的改进,与插入排序不同的是它优先比较距离较远的元素,又叫“递减增量排序”
是针对插入排序以下2特点改进:在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率;一般来说是低效的,因为插入排序每次只能将数据移动一位
希尔排序通过将比较的全部元素分为几个区域来提升插入排序的性能。这样可以让一个元素可以一次性地朝最终位置前进一大步
gap概念,gap递减,最后一步就是插入排序,但是此时几乎已经排好序了
gap是希尔排序核心
出名的gap设置marcin ciura's gap sequence ,gaps = [701, 301, 132, 57, 23, 10, 4, 1],不过下面例子用数组长度不断整除2得到的序列作为gaps
时间复杂度:平均o(n(logn)^2) 最差o(n^2) 最好o(n)
空间复杂度:o(1)
不稳定
#测试输入数组
alist = [27, 33, 28, 4, 2, 26, 13, 35, 8, 14]
#希尔排序
gap = len(alist)//2
while gap>0:
#对每个gap做插入排序
for i in range(gap,len(alist)):
j = i
while j>=gap and alist[j-gap]>alist[j]:
alist[j-gap],alist[j] = alist[j],alist[j-gap]
j -= gap
gap = gap//2
#排序结果输出
print('sorted:',alist)
归并排序 merge sort
分而治之的策略来提高性能,是分治法的典型应用
归并排序是一种递归算法,不断将列表拆分为一半
二路归并 多路归并
缺点是在合并过程需要额外存储空间
时间复杂度: 平均o(nlogn) 最差o(nlogn) 最好o(nlogn)
空间复杂度:o(n)
稳定
#测试输入数组
alist = [27, 33, 28, 4, 2, 26, 13, 35, 8, 14]
#归并排序
def merge_sort(ilist):
def merge(left, right):
result = []
while left and right:
result.append((left if left[0] <= right[0] else right).pop(0))
return result + left + right
if len(ilist) <= 1:
return ilist
mid = len(ilist) // 2
return merge(merge_sort(ilist[:mid]), merge_sort(ilist[mid:]))
#排序结果输出
print('sorted:',merge_sort(alist))
快速排序 quick sort
与归并排序相同,采用分治法,而不使用额外存储
是对冒泡排序的改进
通常明显比其他算法更快,因为它的内部循环可以在大部分的架构上很有效的达成
简单的版本和归并排序一样不好,需要额外存储空间,但是可以改为in-place版本,也就不要额外空间了
时间复杂度: 平均o(nlogn) 最差o(n^2) 最好o(nlogn)
空间复杂度:o(nlogn)
不稳定
#测试输入数组
alist = [27, 33, 28, 4, 2, 26, 13, 35, 8, 14]
#快速排序
def quick_sort(ilist):
length = len(ilist)
if length <= 1:
return ilist
else:
# use the last element as the first pivot
pivot = ilist.pop()
# put elements greater than pivot in greater list
# put elements lesser than pivot in lesser list
greater, lesser = [], []
for element in ilist:
if element > pivot:
greater.append(element)
else:
lesser.append(element)
return quick_sort(lesser) + [pivot] + quick_sort(greater)
#排序结果输出
print('sorted:',quick_sort(alist))
堆排序 heap sort
是对选择排序的一种改进
利用堆这种数据结构所设计的算法 近似完全二叉树
堆通常通过一维数组实 大根堆 小根堆
时间复杂度:平均o(nlogn) 最差o(nlogn) 最好o(nlogn)
空间复杂度:o(1)
不稳定
#测试输入数组
alist = [27, 33, 28, 4, 2, 26, 13, 35, 8, 14]
#堆排序
def heapify(unsorted, index, heap_size):
largest = index
left_index = 2 * index + 1
right_index = 2 * index + 2
if left_index < heap_size and unsorted[left_index] > unsorted[largest]:
largest = left_index
if right_index < heap_size and unsorted[right_index] > unsorted[largest]:
largest = right_index
if largest != index:
unsorted[largest], unsorted[index] = unsorted[index], unsorted[largest]
heapify(unsorted, largest, heap_size)
def heap_sort(unsorted):
n = len(unsorted)
for i in range(n // 2 - 1, -1, -1):
heapify(unsorted, i, n)
for i in range(n - 1, 0, -1):
unsorted[0], unsorted[i] = unsorted[i], unsorted[0]
heapify(unsorted, 0, i)
return unsorted
#排序结果输出
print('sorted:',heap_sort(alist))