“”"
Author:Litra
Environment:python3.7
Time:2020.10.06
Decribe: ten sorts(十大排序)
“”"
1、冒泡排序(bubble sort)
“”“冒泡排序的思想在于对无序表进行多趟比较交换
每趟包括了多次两两相邻比较,并将逆序的数据项互换位置,最终能将本趟的最大项就位
经过n-1次排序,实现整表排序
每趟的过程类似于"气泡"在水中不断上浮到水面的过程
“””
冒泡排序代码
def simple_bubbleSort(alist):
for passnum in range(len(alist)-1,0,-1): # n-1趟
for i in range(passnum):
if alist[i] > alist[i+1]:
# 次序交换
temp = alist[i]
alist[i] = alist[i+1]
alist[i+1] = temp
“”“针对上面代码进行算法分析:
无序表初始数据项的排列状况对冒泡排序没有影响。
冒泡排序通常作为时间效率较低的排序算法,来作为其他算法的比较基准。
其效率主要差在每个数据项在找到最终的位置之前,都必须经过多次的比较和交换,其中大部分的操作是无效的。
但有一点优势是:无需额外的空间储存开销。
“””
性能改进
通过监测每趟比对是否发生过交换,可以提前去欸的那个排序是否完成。
如果每趟比对没有发生过任何的交换,说明列表已经排好序,可以提前结束算法。
改进代码:
def short_bubbleSort(alist):
exchanges = True
passnum = len(alist) - 1
while passnum > 0 and exchanges:
exchanges = False
for i in range(passnum):
if alist[i] > alist[i+1]:
exchanges = True
temp = alist[i]
alist[i] = alist[i+1]
alist[i+1] = temp
passnum = passnum - 1
2、选择排序 Selection Sort
“”“选择排序对冒泡排序进行了改进,保留了其基本的多趟比对思路,每趟都使当前最大项就位。
但选择排序对交换进行了削减,相比于冒泡排序每次进行多次交换,选择排序每次仅进行一次交换,
每趟找出最小的一项,并记录其位置,然后将他与第一项交换位置,同时生成一个有序和一个无序号列表(有序是交换后的结果,无序是原先的列表交换后的结果
“””
代码
def selectionSort(alist):
for fillslot in range(len(alist)-1,0,-1):
positionOfMax = 0 # 初始化设置最大项位置在0的位置
for location in range(1,fillslot+1):
if alist[location]>alist[positionOfMax]:
positionOfMax = location
temp = alist[fillslot]
alist[fillslot] = alist[positionOfMax]
alist[positionOfMax] = temp
3、插入排序(Insertion Sort)
学习过程中注意比较与选择排序的异同
“”“插入排序时间复杂度仍然是O(n_2),但是算法思路与冒泡排序、选择排序不同
插入排序需要维持一个已经排好序的子列表,其位置始终在列表的前部,然后逐步扩大整个子列表直到全表。
“””
代码
def insertionSort(alist):
for index in range(1,len(alist)):
# 从第二项开始
currentvalue = alist[index] # 插入新项
position = index
while position > 0 and alist[position -1] > currentvalue:
# 前面的一项大于当前的值
alist[position] = alist[position -1]
position = position - 1 # 比对,移动
alist[position] = currentvalue # 插入新项
4、希尔排序(shell sort)
“”“希尔排序:在上一节插入排序的比对次数,在最好的情况下是O(n),这种情况发生在列表已经是有序的情况。
从这个情况入手,希尔排序以插入排序为基础,对无序表进行间隔划分子列表,每个子列表都执行插入操作。
随着子列表的数量越来越少,无序表的整体会越来越接近有序,从而减少整体排序的比对次数。
“””
代码
def shellSort(alist):
# 间隔设定
sublistcount = len(alist) // 2
while sublistcount > 0:
for startposition in range(sublistcount):
# 子列表分别进行插入排序
gapInsertSort(alist,startposition,sublistcount)
print("After increments of size",sublistcount,"The list is",alist)
sublistcount = sublistcount // 2 # 间隔缩小
def gapInsertSort(alist,start,gap):
for i in range(start + gap,len(alist),gap):
# gap指的是间隔。
currentvalue = alist[i]
position = i
while position >= gap and alist[position-gap]>currentvalue:
alist[position]= alist[position-gap]
position = position - gap
alist[position] = currentvalue
5、归并排序(merge Sort)
“”“下面我们看看分治策略在排序中的应用:归并排序是递归算法,思路是将数据表持续分类成两半,对凉拌分别进行归并排序
“””
“”“递归的基本结束条件是:数据表仅有一个数据项,自然是排好序的。
缩小规模:将数据表分裂成相等的两份,数据规模缩小为原来的一般
调用自身:将两本分别自身调用,然后将分别排好序的凉拌进行归并,得到排好序的数据表
“””
代码
def mergeSort(alist):
# 基本结束条件:数据集仅剩1个数据项
if len(alist) > 1:
mid = len(alist) // 2
lefthalf = alist[:mid]
righthalf = alist[mid:]
# 递归调用
mergeSort(lefthalf)
mergeSort(righthalf)
i = j = k = 0 # 可以理解成一开始我们的数据表就是被分成每个表里面之后一个数据
while i < len(lefthalf) and j < len(righthalf):
if lefthalf[i]<righthalf[j]:
alist[k] = lefthalf[i]
i = i+1
else:
alist[k] = righthalf[j]
j = j+1
while i < len(lefthalf):
alist[k] = lefthalf[i]
i = i+1
k = k+1
while j < len(righthalf):
alist[k] = righthalf[j]
j = j+1
k = k+1
6、快排
def quickSort(alist):
quickSortHelper(alist,0,len(alist)-1)
def quickSortHelper(alist,first,last):
# 主要进行递归
if first < last:
spiltpoint = partition(alist,first,last)
# 递归调用本身
quickSortHelper(alist,first,spiltpoint+1)
quickSortHelper(alist,spiltpoint+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 leftmark <= rightmark and \
alist[rightmark] >= pivotvalue:
rightmark = rightmark - 1 # 向右移动右标签
if rightmark < leftmark:
# 遍历结束
done = True
else:
# 左右标值交换(因为上面的循环的结束条件呀)
temp = alist[leftmark]
alist[leftmark] = alist[rightmark]
alist[rightmark] = temp
# 这里又是为什么呢?--中值和右标签交换位置呀~get√
temp = alist[first]
alist[first] = alist[last]
alist[last] = temp
return rightmark
“”“快排算法分析:
快速排序过程主要分为两部分:分裂和移动。
如果分裂总是能将数据标分为相等的两部分,那么就是O(log n)的复杂度,而移动过程还是需要将每一项与中值进行比较,还是O(n),所以综合起来就是O(nlog n)
而且,算法运行过程中不需要多余的存储空间。
但是如果选取的基值过于偏离中部,则会造成左右两部分数据不平衡。极端情况:有一部分始终没有数据,这样算法的时间复杂度将会退化到O(n_2),还要再加上递归的开销,简直比冒泡还糟糕。
因此,可以适当改进选取中值的方法,让中值更具有代表性。
比如:三点取样,从数据的头、中、尾许纳区中值。这样虽然会产生一些额外的时间开销,仍然不能排除极端情况的发生。
“””
7、堆排序
8、桶排序
9、计数排序
10、基数排序
参考资料
北京大学数据结构与算法网课资料