文章目录
引言
排序这部分,会介绍9种基础的排序及其优化
- 计数排序
- 基数排序
- 冒泡排序
- 桶排序
- 选择排序
- 快速排序
- 插入排序
- 归并排序
- 希尔排序
一、冒泡排序
冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。冒泡排序总结来说,就是相邻对比交换位置,直至无需再交换。其动画演示为:
# 冒泡排序
def bubble_sort(alist):
'''冒泡排序'''
n = len(alist)
count = 0
for j in range(0, n - 1):
swap = False
print(alist)
# 经过j,确定了j个元素,后面的不需要再比较
for i in range(0, n - 1 - j):
count += 1
# i指的是下标,从头走到尾,走到n-2,与n-1比较即可
if alist[i] > alist[i + 1]:
swap = True
alist[i], alist[i + 1] = alist[i + 1], alist[i]
# swap = False时,表明无需再交换,直接返回
if not swap:
break
print(f"总循环次数{count}")
return alist
if __name__ == '__main__':
li = [3, 4, 2, 1, 5, 6, 7, 8]
print('bubble_sort end:', bubble_sort(li))
[3, 4, 2, 1, 5, 6, 7, 8]
[3, 2, 1, 4, 5, 6, 7, 8]
[2, 1, 3, 4, 5, 6, 7, 8]
[1, 2, 3, 4, 5, 6, 7, 8]
总循环次数22
bubble_sort end: [1, 2, 3, 4, 5, 6, 7, 8]
代码优化:
优化思路︰在排序的过程中,数据可以从中间分为两段,一段是无序状态,另一段是有序状态。每一次循环的过程中,记录最后一个交换元素,它便是有序和无序状态的边界
下一次仅循环到边界即可,从而减少循环次数,达到优化。当然如果没有有序部分,则不会减少循环次数。
# 冒泡排序
def bubble_sort(alist):
'''冒泡排序'''
n = len(alist)
count = 0
# 记录最后一个交换元素坐标
last_change_index = 0
# 有序与无序的分界线
border = n - 1
for j in range(0, n - 1):
swap = False
print(alist)
# 经过j,确定了j个元素,后面的不需要再比较
for i in range(0, border):
count += 1
# i指的是下标,从头走到尾,走到n-2,与n-1比较即可
if alist[i] > alist[i + 1]:
swap = True
alist[i], alist[i + 1] = alist[i + 1], alist[i]
last_change_index = i
# swap = False时,表明无需再交换,直接返回
if not swap:
break
border = last_change_index
print(f"总循环次数{count}")
return alist
if __name__ == '__main__':
li = [3, 4, 2, 1, 5, 6, 7, 8]
print('bubble_sort end:', bubble_sort(li))
[3, 4, 2, 1, 5, 6, 7, 8]
[3, 2, 1, 4, 5, 6, 7, 8]
[2, 1, 3, 4, 5, 6, 7, 8]
[1, 2, 3, 4, 5, 6, 7, 8]
总循环次数10
bubble_sort end: [1, 2, 3, 4, 5, 6, 7, 8]
时间复杂度:
- 最优时间复杂度:O(n) (表示遍历一次发现没有任何可以交换的元素,排序结束。)
- 最坏时间复杂度:O(n^2)(数据一开始就是倒序的,需要经过n-1次冒泡)
空间复杂度:
- 空间复杂度是O(1),这类算法也可以叫做原地排序算法。
排序算法的稳定性:
- 稳定性怎么理解呢?
如果待排序的序列中存在值相等的元素,经过排序之后,相等元素之间原有的先后顺序不变。
总结:
冒泡排序虽然使用了数组存储数据但是并没有使用数组随机访问的特性,因此改用链表这种存健结构,使用冒泡排序仍然是可以实现的。
二、选择排序
选择排序(Selection sort)是一种简单直观的排序算法。其工作原理为:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。通俗点理解就是拿出最大或最小,然后在剩余中拿出最大或最小。其动画演示为:
选择排序的主要优点与数据移动有关。如果某个元素位于正确的最终位置上,则它不会被移动。选择排序每次交换一对元素,它们当中至少有一个将被移到其最终位置上,因此对n个元素的表进行排序总共进行至多n-1次交换。在所有的完全依靠交换去移动元素的排序方法中,选择排序属于非常好的一种。
# 选择排序策略是拿出最小,然后在剩余中拿出最小
def selection_sort(aList):
n = len(aList)
count = 0
# 需要n-1次遍历寻找
for i in range(0, n - 1):
print(aList)
# 存储最小值下标
min_index = i
for j in range(i + 1, n):
count += 1
if aList[min_index] > aList[j]:
min_index = j
# 判断min_index与i的关系
if min_index != i:
# 元素互换
aList[i], aList[min_index] = aList[min_index], aList[i]
print(f"总循环次数为{count}")
return aList
if __name__ == '__main__':
li = [3, 4, 2, 1, 8, 6, 5, 7]
print('select_sort end:', selection_sort(li))
[3, 4, 2, 1, 8, 6, 5, 7]
[1, 4, 2, 3, 8, 6, 5, 7]
[1, 2, 4, 3, 8, 6, 5, 7]
[1, 2, 3, 4, 8, 6, 5, 7]
[1, 2, 3, 4, 8, 6, 5, 7]
[1, 2, 3, 4, 5, 6, 8, 7]
[1, 2, 3, 4, 5, 6, 8, 7]
总循环次数为28
select_sort end: [1, 2, 3, 4, 5, 6, 7, 8]
代码优化版本:
在开头:拿大,然后剩余部分取大;尾部:拿小,然后剩余部分取小,可以对代码进行优化,减少循环次数。优化点是开头末尾同时选择排序,当最小值的索引 + 1 = 尾部有序部分第一个元素索引
时,表明已经排好序了。
# 选择排序策略是拿出最小,然后在剩余中拿出最小
def selection_sort(aList):
n = len(aList)
count = 0
# 需要n-1次遍历寻找
for i in range(0, n - 1):
print(aList)
# 存储最小值下标
min_index = i
# 存储最大值下标
max_index = n - i - 1
for j in range(i + 1, n - i - 1):
count += 1
# 更新最小值索引
if aList[min_index] > aList[j]:
min_index = j
# 更新最大值索引
if aList[max_index] < aList[j]:
max_index = j
if max_index == min_index + 1:
break
# 判断min_index与i的关系
if min_index != i:
# 元素互换
aList[i], aList[min_index] = aList[min_index], aList[i]
# 判断max_index与i的关系
if max_index != n - i - 1:
# 元素互换
aList[max_index], aList[n - i - 1] = aList[n - i - 1], aList[max_index]
print(f"总循环次数为{count}")
return aList
if __name__ == '__main__':
li = [3, 4, 2, 1, 8, 6, 5, 7]
print('select_sort end:', selection_sort(li))
[3, 4, 2, 1, 8, 6, 5, 7]
总循环次数为6
select_sort end: [3, 4, 2, 1, 8, 6, 5, 7]
时间复杂度:
- 选择排序无论数据初始是何种状态,均需要在未排序元素中选择最小或最大元素与未排序序列中的首尾元素交换,因此它的最好、最坏、平均时间复杂度均为 O ( n 2 ) O(n^2) O(n2)。
稳定性:
- 选择排序在未排序区间选择一个最小值,与前面的元素交换,对于值相同的元素,因为交换会破坏他们的相对顺序,因此它是一种不稳定的排序算法。
空间复杂度:
- 只需要选择一个变量作为交换,因此,空间复杂度为O(1),是一种原地排序算法。
三、插入排序
插入排序(英语:Insertion Sort)是一种简单直观的排序算法。其工作原理为:通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。通俗来讲,就是将未排序的,在已排序中找到位置插入进去。其动画演示为:
def insert_sort(aList):
count = 0
n = len(aList)
# 从第1个位置处开始遍历
for i in range(1, n):
print(aList)
# 中间变量
tmp = aList[i]
j = i
# j从后往前遍历,寻找合适插入位置
while j > 0:
count += 1
if tmp < aList[j - 1]:
aList[j] = aList[j - 1]
else:
break
j -= 1
# 循环结束时,j处是合适的插入位置
aList[j] = tmp
print(f'总循环次数{count}')
return aList
if __name__ == '__main__':
li = [1, 3, 4, 2, 1, 5, 6, 7, 8, 4]
print(&