LowB三人组——冒泡排序、插入排序、选择排序
牛 逼三人组——快速排序、归并排序、堆排序、希尔排序(另外的)
1、冒泡排序法:
按寻常理解,是要相邻的元素进行比较,将极值逐个放到末尾,寻常代码如下
# 咕噜咕噜往后排
# 传统冒泡排序法
def bubble_sort(lst):
for i in range(len(lst)):
for j in range(i+1,len(lst)):
if lst[j-1]>lst[j]:
lst[j],lst[j-1] = lst[j-1],lst[j]
print(lst)
lst = [1,3,5,2,6]
bubble_sort(lst)
但有一种排序,也是逐个逐个对比,只是把极值放在头部,代码如下
# 类似冒泡排序的排头法
def like_bubble_sort(lst):
for i in range(len(lst)):
for j in range(i+1, len(lst)):
if lst[i]>lst[j]:
lst[i],lst[j] = lst[j],lst[i]
print(lst)
return lst
lst = [1,3,5,2,6]
like_bubble_sort(lst)
第二种与传统冒泡排序法,极值位置和对比位置虽有不同,但排序的过程都是两两相互比较的,并且时间复杂度、空间复杂度,都是一样的。因此,第二种排序,可以说是传统冒泡排序的延伸,但实际应用中又略有不同,姑且称第二种类似冒泡的排序法,成为“排头法”吧(生动一些,我也不知道有没有其他专业人士给专门命名了)。
时间复杂度:每个元素都要跟剩余元素比较,所以比较次数是n*(n-1)=n^2-n。根据以下时间复杂度的大O表示法,冒泡排序时间复杂度为O(n ^ 2)
【大O和小O在数学中分别表示极限大和极限小,可搜索了解】
实际应用的优化——冒泡排序法
但上述的冒泡排序法实际应用是不够高效的,当某一轮排序过程中,不再产生相邻元素的交换,说明,每一个元素都比前一个元素大(或小),比后一个元素小(或大)。这时候,排序已提前完成,无需再进行下一轮的排序。
优化后的代码如下
# 优化后的冒泡排序法
def bubble_sort(lst):
for i in range(len(lst)-1):
# 每轮循环前设置控制变量stop为True
stop = True
for j in range(i,len(lst)-1):
if lst[j+1]>lst[j]:
lst[j],lst[j+1] = lst[j+1],lst[j]
# 如果每轮相邻元素的对比中,有交换,则设置控制变量stop为False,表示不能停止排序
stop = False
print(lst)
# 每轮循环比较后,判断stop是否依然是True,如果是True,表示比较过程没有交换,则立刻停止冒泡排序
if stop:
return lst
lst = [1,3,5,2,6]
bubble_sort(lst)
优化后的冒泡排序法,只需要进行3轮的比较
但之前所说的类似于冒泡排序法的“排头法”就不能这么优化了,因为排头法虽然也是两两进行比较,但却不是相邻元素的比较,只是头部元素与后续每一个元素的比较,如果一轮排序下来,无元素交换,也只能说明头部元素是极值,却不能说明相邻元素之间的顺序已确定。因此如果盲目使用优化的方法,会导致结果出错的。
# “优化后” 的排头法
def like_bubble_sort(lst):
for i in range(len(lst)):
break_key = True
for j in range(i+1, len(lst)):
if lst[i]>lst[j]:
lst[i],lst[j] = lst[j],lst[i]
break_key = False
print(lst)
if break_key:
break
return lst
lst = [1,3,5,2,6]
like_bubble_sort(lst)
如果是用之前优化传统冒泡排序法的方法,这个排头法对比一次就结束了,因为原列表的头部数字1,在与后续元素比较时,都是最小的,这样第一轮排序就没有元素交换,这时如果停止后续的排序,那么结果就会出错。
选择排序法
算法有多讨厌呢,大概就是曾经夜郎自大地与别人讨论,“排头法”其实就是冒泡排序的衍生物后,发现。。。原来 “排头法” ,学名 “选择排序法”,尴尬,尴了个大尬。
在记录冒泡排序法衍生出的“排头法”,其实原理就是“选择排序法”。可怕吗,哈哈哈哈哈哈,不可怕,就是有点儿尴尬。
选择排序法的原理就是,极值往前排。
实现的方法有好几种,但主要说明以下两种
第一种就是上边冒泡尴尬了的“排头法”,效率非常低,每次都要交换
# 选择排序法
def select_sort(lst):
for i in range(len(lst)):
for j in range(i+1, len(lst)):
if lst[i]>lst[j]:
lst[i],lst[j] = lst[j],lst[i]
print(lst)
lst = [1,3,5,2,6]
select_sort(lst)
第二种,相对好一些,至少是遇到极值先记下坐标,当一轮比较下来后,将极值坐标与头部坐标交换即可。这样就节省了交换的次数
# 优化后的选择排序法
def select_sort(lst):
for i in range(len(lst)):
min_index = i
for j in range(i+1, len(lst)):
if lst[i]>lst[j]:
min_index = j
lst[i],lst[min_index] = lst[min_index],lst[i]
print(lst)
lst = [1,3,5,2,6]
select_sort(lst)
但依然发现,无论优化与否,选择排序的比较次数都一样,并且案例中第3轮排序其实已经是最后的顺序,却还是要继续完成后两轮的排序。
因此,选择排序暂时没有更好的优化方式。
(吐槽,选择排序,取名一点儿都不生动)
选择排序的时间复杂度:O(n^2)
插入排序法
插入排序:分隔两列,前列已排序,后列未排序,后列逐个向前列排序
# 插入排序法
def insert_sort(lst):
length = len(lst)
for i in range(length):
j = i+1
# 前序
while j>0:
if lst[j]<lst[j-1]:
lst[j],lst[j-1] = lst[j-1],lst[j]
j -= 1
else:
break
return lst
插入排序法的时间复杂度:O(n^2)