Python数据结构之基本排序算法(一)

前言

本篇文章以图文结合的方式,分析了选择排序法、冒泡排序法和插入排序法的逻辑,给出了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【15 12 41 11 32 

Round【25 11 41 12 32 

Round【35 11 12 41 32 

Round【45 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 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Larissa857

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值