一、冒泡排序
原理:
- 比较相邻的元素。如果第一个比第二个大就交换他们两个
- 每一对相邻元素做同样的工作,直到结尾最后一对
- 每个元素都重复以上步骤,除了最后一个
第一步:
将乱序中的最大值找出,逐一移到序列最后的位置
alist = [3, 5, 9, 2, 1, 7, 8, 6, 4]
def bubble_sort(alist):
# 找最大值的方式是通过对列表中的元素进行两两比较,值大的元素逐步向后移动
# 序列中有n个元素,两两比较的话,需要比较n-1次
for i in range(len(alist) - 1): # 循环n-1次,控制两两比较的次数
if alist[i] > alist[i + 1]:
# 如果前面的元素大于后面的元素,交换两个元素的位置,否则不做任何操作
alist[i], alist[i + 1] = alist[i + 1], alist[i]
return alist
print(bubble_sort(alist))
# 输出:最大值已经移动到最右边了
[3, 5, 2, 1, 6, 7, 8, 4, 9]
当上述代码已经可以将序列中的最大值放置到合适的位置,然后我们就可以将上述操作继续作用到 n-1 和元素对应的新序列,则就可以将 n-1 个元素对应的最大值放置到了 n-1 和元素的最后位置。
结论:发现如果将上述的操作逐步的作用 n-1 此就可以将整个序列变成有序的。
第二步:
将第一步的操作继续作用 n-1 次
alist = [3, 5, 9, 2, 1, 7, 8, 6, 4]
def bubble_sort(alist):
for j in range(len(alist)-1): # 外层循环次数递增,内层循环次数递减
for i in range(len(alist) - 1-j): # 循环次数需要递减-j,控制两两比较的次数
if alist[i] > alist[i + 1]:
# 如果前面的元素大于后面的元素,交换两个元素的位置,否则不做任何操作
alist[i], alist[i + 1] = alist[i + 1], alist[i]
return alist
print(bubble_sort(alist))
# 输出
[1, 2, 3, 4, 5, 6, 7, 8, 9]
二、选择排序
思路:
- 首先在序列中找到最大(小)元素,存放到序列的最后
- 在从剩余的序列元素中继续找最大(小)的元素,放到序列中上一个最大值的前一个位置
- 重复第二步,直到所有元素排序完毕
第一步:
将乱序中的元素两两比较,找出最大值,然后直接将最大值放置到序列最后的位置(将最大值直接和最后一个元素交换位置)
def select_sort(alist):
max_index = 0 # 最大值元素的下标,一开始假设下标为0的元素为最大值
for i in range(len(alist) - 1): # 循环控制两两比较的次数
# 如果在比较的过程中发现,下标为max_index不是最大值,那么就改变max_index
if alist[max_index] < alist[i + 1]:
max_index = i + 1
# 循环结束后max_index就一定是最大值的下标,并且把该数和最后一个值做交换
alist[len(alist) - 1], alist[max_index] = alist[max_index], alist[len(alist) - 1]
return alist
alist = [3, 5, 9, 2, 1, 7, 8, 6, 4]
print(select_sort(alist))
# 输出
[3, 5, 4, 2, 1, 7, 8, 6, 9]
第二步:
将第一步继续作用 n-1 次
def select_sort(alist):
for j in range(len(alist) - 1):# 外层循环递增n-1次
max_index = 0 # 最大值元素的下标,一开始假设下标为0的元素为最大值
for i in range(len(alist) - 1 - j): # 内层循环递减,循环控制两两比较的次数
# 如果在比较的过程中发现,下标为max_index不是最大值,那么就改变max_index
if alist[max_index] < alist[i + 1]:
max_index = i + 1
# 循环结束后max_index就一定是最大值的下标,并且把该数和最后一个值做交换
alist[len(alist) - 1 - j], alist[max_index] = alist[max_index], alist[len(alist) - 1 - j]
return alist
alist = [3, 5, 9, 2, 1, 7, 8, 6, 4]
print(select_sort(alist))
三、插入排序
思路:
- 需要将原始序列分为两个部分:有序部分、无序部分。
- 将无序部分中的元素逐一插入到有序部分中
注意:初始情况下,有序部分为乱序序列中的第一个元素,无序部分为乱序序列的 n-1 个元素
例如:
# 乱序序列:[8,3,5,7,6]
[8, 3,5,7,6] # 8就是初始的有序部分,3、5、7、6就是初始的无序部分
[3,8, 5,7,6]
[3,5,8, 7,6]
[3,5,7,8, 6]
[3,5,7,6,8, ]
第一步:
定义一个变量 i ,i 表示的是有序部分元素的个数和无序部分第一个元素小标
alist = [8, 3, 1, 6, 7]
i = 1 # i 就是有序部分元素的个数和无序部分第一个元素下标
# alist[i-1]:有序部分最后一个元素下标
# alist[i]:无序部分第一个元素下标
if alist[i - 1] > alist[i]:
alist[i], alist[i - 1] = alist[i - 1], alist[i]
# [3, 8, 1, 6, 7]
第二步:循环作用到每个元素中
alist = [8, 3, 1, 6, 7]
i = 2
# alist[i-1]:有序部分最后一个元素下标
# alist[i]:无序部分第一个元素下标
while i > 0:
if alist[i - 1] > alist[i]:
# 循环第一次时[3,1,8, 6,7]
alist[i], alist[i - 1] = alist[i - 1], alist[i]
i -= 1
# 循环继续
# [1,3,8, 6,7]
else:
break
第三步:
处理变量 i,需要让 i 进行自己递增
for i in range(1, len(alist)): # i = [1,2,3,4]
# alist[i-1]:有序部分最后一个元素下标
# alist[i]:无序部分第一个元素下标
while i > 0:
if alist[i - 1] > alist[i]:
alist[i], alist[i - 1] = alist[i - 1], alist[i]
i -= 1
else:
break
完整代码:
def insert_sort(alist):
for i in range(1, len(alist)):
while i > 0:
if alist[i - 1] > alist[i]:
alist[i - 1], alist[i] = alist[i], alist[i - 1]
i -= 1
else:
break
return alist
四、希尔排序
关键变量:增量gap
gap:初始值为 len(alist) // 2
- 表示分组的组数
- 每一组数据之间的间隔
插入排序就是增量为 1 的希尔排序
第一步:
将插入排序代码写出
def hill_sort(alist):
for i in range(1, len(alist)):
while i > 0:
if alist[i - 1] > alist[i]:
alist[i - 1], alist[i] = alist[i], alist[i - 1]
i -= 1
else:
break
return alist
第二步:
在插入排序代码中加入增量的概念
def hill_sort(alist):
gap = len(alist) // 2 # 初识增量
# 将插入排序中的增量1替换成gap
# 由增量1变成了增量为gap了
for i in range(gap, len(alist)):
while i > 0:
if alist[i - gap] > alist[i]:
alist[i - gap], alist[i] = alist[i], alist[i - gap]
i -= gap
else:
break
return alist
第三步:
在第二步中进行增量的缩减(增量缩减到1结束)完整代码
def hill_sort(alist):
gap = len(alist) // 2 # 初识增量
while gap >= 1:
for i in range(gap, len(alist)):
while i > 0:
if alist[i - gap] > alist[i]:
alist[i - gap], alist[i] = alist[i], alist[i - gap]
i -= gap
else:
break
gap //= 2 # 缩减增量
return alist
五、快速排序
思路:
- 将列表中第一个元素设定为基准数字,赋值给mid变量,然后将整个列表中比基准小的数值放在基准的左侧,比基准大的数字放在基准右侧,然后将基准数字左右两侧的序列在根据此方法进行排放
- 定义两个指针,low 指向最左侧,high 指向最右侧
- 然后对最右侧指针进行向左移动,移动法则是,如果指针指向的数值比基准小,则将指针指向的数字移动到基准数字原始位置,否则继续移动指针。
- 如果最右侧指针指向的数值移动到基准位置时,开始移动最左侧指针,将其向右移动,如果该指针指向的数值大于基准侧将该数值移动到最右侧指针指向的位置,然后停止移动。
- 如果左右侧指针重复则,将基准放入左右指针重复的位置,则基准左侧为比其小的数值,右侧为比其大的数值
第一步:
核心操作,将基数 mid 放置到序列中间,使得基数左侧都是比它小的,右侧是比它大的
def quick_sort(alist):
low = 0 # 第一个元素下标
high = len(alist) - 1 # 最后一个元素下标
mid = alist[low] # 基数:初始值为序列中的第一个数值
while low != high:
# 先移动high
while low < high:
if mid < alist[high]: # 下标high对应的值大于mid,high就向右偏移1
high = high - 1
else:
# 否则,就把将high指向的数值放置到左侧下标为low对应的空位
alist[low] = alist[high]
break # 传递后high下标偏移结束
# 开始移动low
while low < high:
if mid > alist[low]: # 下标low对应的值小于mid,low就向左偏移1
low = low + 1
else:
# 否则,就把将low指向的数值放置到左侧下标为high对应的空位
alist[high] = alist[low]
break # 并结束
# 最后当low和high相等的时候,那么就把mid传给下标为low或high的位置
alist[low] = mid
return alist
alist = [6, 1, 2, 7, 9, 3, 4, 5, 10, 8]
print(quick_sort(alist))
# 输出——>6左边都是比6小的,右边都是比6大的
[5, 1, 2, 4, 3, 6, 9, 7, 10, 8]
第二步:
将第一步的核心操作递归作用到基数的左右两侧的子序列中
# 那么如何区分根据基数拆分出的左右子序列呢?可以通过传入指定的left和right来指定不同的子序列
def quick_sort(alist, left, right):
low = left # 第一个元素下标
high = right # 最后一个元素下标
if low > high: # 递归结束条件,low是不能大于high的
return
mid = alist[low] # 基数:初始值为序列中的第一个数值
while low != high:
# 先移动high
while low < high:
if mid < alist[high]: # 下标high对应的值大于mid,high就向右偏移1
high -= 1
else:
# 否则,就把将high指向的数值放置到左侧下标为low对应的空位
alist[low] = alist[high]
break # 传递后high下标偏移结束
# 开始移动low
while low < high:
if mid >= alist[low]: # 下标low对应的值小于mid,low就向左偏移1
low += 1
else:
# 否则,就把将low指向的数值放置到左侧下标为high对应的空位
alist[high] = alist[low]
break # 并结束
# 最后当low和high相等的时候,那么就把mid传给下标为low或high的位置
if low == high:
alist[low] = mid
# 上述为核心操作,需要将核心操作递归作用到左右子序列中
quick_sort(alist, left, low - 1) # 递归到左侧序列中
quick_sort(alist, high + 1, right) # 递归到右侧序列中
return alist
alist = [6, 1, 2, 7, 9, 3, 4, 5, 10, 8]
print(quick_sort(alist, 0, len(alist) - 1))
# 输出
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]