前言
本篇文章以图文结合的方式,分析了选择排序法、冒泡排序法和插入排序法的逻辑,给出了Python实现代码及其复杂度分析。参考自书籍 Fundamentals of Python Data Structure ,本篇文章代码均已测试通过。
引入
在正式介绍这三种算法之前,首先让我们考虑下排序算法需要做什么事?字面上看,这问题不是太哈批了吗?🫤排序算法当然是用来排序的啊😓再深入一点考虑呢?对谁排序(使用什么数据类型)?排序的步骤是怎样的(一个一个排还是先组合后排序)?怎样的排序是最理想的(也即复杂度分析,考虑算法优化)?
👉先考虑第一个问题:Python的基本数据类型共有六种,能作为容器去存储数值型数据(整型和浮点型)的除去Tupple(不可变)、Number、String,还剩下Dict、Set和List,Set(集)和Dict(字典)是无序的、与排序算法设计目的冲突不考虑,因此一圈排除下来就只剩下List(列表了),而且列表是异构的、元素可根据索引有序排列,可作为排序算法的基本数据类型。
【注:在后续的学习中,我们会接触到各种具体的数据类型如队列、栈等,它们都是基于六大基本数据类型的各种变体,如二维数组是由两个列表在空间维度上拼接形成的】
👉再考虑第二个问题:排序的基本过程是先搜索整个列表,然后比较找出最小值依次从左往右排,这是 最简单的“顺序搜索+寻找最小值+交换两者位置” 的思想,然而,根据问题的复杂度,我们还可以选择更有效率的 “二叉搜索” 方法;当问题变得更复杂时,我们可以根据具体问题的复杂度分析提出优化效果更好的算法。这也是我们要对每个算法进行复杂度分析的原因,而第三个问题我们将留在接下来“基本排序算法”系列文章中讲解。
现在,让我们快速地构建一个最简单的列表排序法:
👉首先实现“顺序搜索目标值”:
""" 顺序搜索一个列表,如果找到目标值则返回其索引,否则返回-1 """
def sequentialSearch(target, lyst):
""" Returns the position of the target in the lyst if found,
or -1 otherwise """
position = 0
while position < len(lyst):
if target == lyst[position]:
return position
position += 1
return -1
"""渐进复杂度:
O(n)
"""
👉用二分查找优化顺序搜索:
""" 有序列表的二叉搜索(相似于折半查找法) """
def binarySearch(target, sorrtedLyst):
left = 0
right = len(sorrtedLyst) - 1
while left < right:
midpoint = (left + right) // 2
if target == sorrtedLyst[midpoint]:
return midpoint
elif target < sorrtedLyst[midpoint]:
right = midpoint - 1
else:
left = midpoint + 1
return -1
"""渐进复杂度:
O(lgn)
"""
👉其次实现“返回最小值索引位置”:
""" 分析流程:
1.设置一个标志索引变量,记录最小值的索引值,初始为0
2.遍历该列表
->如果当前索引下的值小于标志索引下的值,则赋当前值给标志索引变量
->否则,不作任何操作
3.打印列表中标志索引对应的值
"""
# 写一个函数实现上述分析
def indexOfMin(lyst):
"""Return the index of minimum item"""
minIndex = 0
currentIndex = 0
while currentIndex < len(lyst):
if lyst[currentIndex] < lyst[minIndex]:
minIndex = currentIndex
currentIndex += 1
return minIndex
"""渐进复杂度:
O(n)
"""
👉然后实现“交换两者位置”:
def swap(lyst, i, j):
""" Exchange the items at position i and j """
temp = lyst[i]
lyst[i] = lyst[j]
lyst[j] = temp
"""渐进复杂度:
O(1)
"""
👉最后,让我们调用这三个功能函数(在顺序搜索里调用两个功能函数),以完成列表[5, 3, 1, 2, 4]的排序:
lyst = [5, 3, 1, 2, 4]
def sorted(lyst):
boundary = 0
for i in range(1, len(lyst)):
while boundary < i:
minIndex = indexOfMin(lyst[boundary:])
swap(lyst, boundary, minIndex+i-1)
boundary += 1
return lyst
sorted(lyst)
"""渐进复杂度:
O(n^2)
"""
👉用二分查找优化过后的实现:合并排序(见下一篇文章)
通过上述的分析和实现,我们可以对算法设计的流程有一个大概的了解,那么接下来,我们就具体来学习基本排序算法的Python实现。
注:在实现过程中的基本功能函数swap()
在下文中会用到。
一、选择排序
选择排序算法(selection sort algorithm)基本思想:搜索整个列表,找到最小项的位置;如果该位置不是列表的第一个位置(即索引值为0),就会交换这两位置的值。
📚图解如下所示:
💻代码实现:
print("{0:=^30}".format("选择排序法"))
""" 选择排序法:最小的前移,其复杂度为O(n^2) """
def selectionSort(lyst):
i = 0
while i < len(lyst) - 1:
minIndex = i
j = i + 1
while j < len(lyst):
if lyst[j] < lyst[minIndex]:
minIndex = j
j += 1
if minIndex != i:
swap(lyst, minIndex, i)
i += 1
print("Round【%d】" % i)
for k in range(len(lyst)):
print("%d " % lyst[k], end="")
print("\n")
# return lyst
s1 = selectionSort([32, 12, 41, 11, 5])
print(type(s1))
# for i in range(len(s)):
# print("%d " % s[i])
👀输出为:
============选择排序法=============
Round【1】
5 12 41 11 32
Round【2】
5 11 41 12 32
Round【3】
5 11 12 41 32
Round【4】
5 11 12 32 41
<class 'NoneType'>
二、冒泡排序
冒泡排序(bubble sort):从列表索引为0处开始,并比较每一对数据项,直到移动到列表的末尾。每当成堆的两项之间的顺序不正确时(左大右小),就会交换位置,整体上表现为将最大的项移动到列表末尾。
📚图解如下所示:
💻代码实现:
print("{0:=^30}".format("冒泡排序法"))
""" 冒泡排序法:最大的沉底,其复杂度为O(n^2) """
def bubbleSort(lyst):
n = len(lyst)
while n > 1:
i = 1
while i < n:
if lyst[i] < lyst[i-1]:
swap(lyst, i ,i-1)
i += 1
n -= 1
for k in range(len(lyst)):
print("%d " % lyst[k], end="")
print("\n")
s2 = bubbleSort([32, 12, 41, 11, 5])
👀输出为:
============冒泡排序法=============
12 32 11 5 41
12 11 5 32 41
11 5 12 32 41
5 11 12 32 41
三 、插入排序
插入排序(Insertion sort):在第i轮通过列表时,第i个项应该插入到列表的前i个项之中的正确排序位置。
📚图解如下所示:
💻代码实现:
print("{0:=^30}".format("插入排序法"))
""" 插入排序法:最大的沉底,其复杂度为O(n^2) """
def insertionSort(lyst):
i = 1
while i < len(lyst):
itemToInsert = lyst[i] # 此处如果不定义itemToInsert来接收lyst[i]的值的话,整个数组将会重复上一个较大的元素
j = i - 1
while j >= 0:
if itemToInsert < lyst[j]:
lyst[j + 1] = lyst[j]
j -= 1
else:
break
lyst[j + 1] = itemToInsert
i += 1
for k in range(len(lyst)):
print("%d " % lyst[k], end="")
print("\n")
s3 = insertionSort([32, 12, 41, 11, 5])
👀输出为:
============插入排序法=============
12 32 41 11 5
12 32 41 11 5
11 12 32 41 5
5 11 12 32 41