Python数据结构学习笔记——搜索与排序算法

简介: Python数据结构学习笔记——搜索与排序算法

一、搜索


(一)搜索的方法


搜索是指从元素集合中找到某个特定元素的算法过程,搜索过程通常返回True或False,分别表示元素是否存在。Python中通过运算符in来检查一个元素是否处于元素集合中,如下:

print("C" in ["C", "S", "D", "N"])
print("C" in (1, 2, 3))
print("c" in {"a": "A", "b": "B", "c": "C"})
print("c" in {1, "123", "ab"})

运行结果如下:

1667134010074.jpg


(二)顺序搜索


以python中的列表为例,由于其各元素存在线性或顺序的关系,每个元素都有一个唯一的下标,且下标是有序的,可以通过顺序访问,从而进行顺序搜索。顺序搜索的概念是从左到右依次查找元素,从第一个元素开始依次,若到列表末尾仍没有查到则说明该元素不在列表中。

无序列表的顺序搜索和有序列表的顺序搜索的时间复杂度都是O(n),只有当列表中不存在要搜索的目标元素时,有序排列的元素的顺序搜索的效率较高。

无序列表的顺序搜索代码如下:

# 无序列表的顺序搜索
def sequential_Search(alist, item):  # 传入两个参数:一个无序列表及要搜索的元素
    pos = 0  # 下标值从0开始
    found = False
    while pos < len(alist) and not found:  # 直到搜索到元素循环停止
        if alist[pos] == item:  # 若当前下标值对应的元素等于要搜索的元素,found变量值为True
            found = True
        else:
            pos = pos + 1  # 下标值加一,继续搜索
    return found
# 有序列表的顺序搜索
def orderedSequential_Search(alist, item):  # 传入两个参数:一个有序列表及要搜索的元素
    pos = 0  # 下标值从0开始
    found = False
    stop = False
    while pos < len(alist) and not found and not stop:
        if alist[pos] == item:  # 若当前下标值对应的元素等于要搜索的元素,found变量等于True
            found = True
        else:
            if alist[pos] > item:  # 若当前下标值大于要搜索的元素,则stop变量等于True
                stop = True
            else:
                pos = pos + 1  # 下标值加一,继续搜索
    return found
print(sequential_Search([6, 4, 5, 0], 6))
print(orderedSequential_Search([1, 2, 3, 4, 5], 6))

运行结果如下:

1667134051348.jpg


(三)二分搜索


二分搜索相较于顺序搜索,其不是从第一个元素开始搜索列表,而是从中间的元素着手,所以被称为二分搜索。若这个元素就是要搜索的目标元素,则立即停止搜索;如果不是,则可以利用列表有序的特性,排除一半的元素,依次下去,直到搜索到目标元素。简单的来说,也就是将一个大问题依次分成小问题,从而缩小范围,依次解决。

对于有序列表来说,二分搜索算法在最坏情况下的时间复杂度是O(logn),有序列表的二分搜索代码如下:

# 有序列表的二分搜索
def binarySearch(alist, item):  # 传入两个参数:一个有序列表及要搜索的元素
    first = 0
    last = len(alist) - 1  # 列表的最后一个元素的下标值
    found = False
    while first <= last and not found:
        midpoint = (first + last) // 2  # 整除进行二分
        if alist[midpoint] == item:
            found = True
        else:
            if item < alist[midpoint]:
                last = midpoint - 1
            else:
                first = midpoint + 1
    return found
print(binarySearch([1, 6, 9, 10, 13, 14, 18], 14))

运行结果如下:

1667134084412.jpg

步骤解析:

1667134094774.jpg

二分搜索中运用递归方法进行改进,代码如下:

#二分搜索的递归方法
def binarySearch(alist, item):
    if len(alist) == 0:
        return False
    else:
        midpoint = len(alist) // 2
        if alist[midpoint] == item:
            return True
        else:
            if item < alist[midpoint]:
                return binarySearch(alist[:midpoint], item)  # alist[:midpoint]表示从开头取到midpoint下标值
            else:
                return binarySearch(alist[midpoint + 1:], item)  #alist[midpoint + 1:]表示从结尾取到midpoint+1下标值
print(binarySearch([1, 6, 9, 10, 13, 14, 18], 14))

运行结果如下:

1667134114713.jpg


二、排序


内排序和外排序


根据待排序的内容是否在内存中可将排序分为内排序和外排序,内排序的整个排序过程在内存在进行,而外排序由于排序的内容太多,内存无法容纳,需要借助外存,即其排序过程在内、外存之间交换数据才能进行。


(一)冒泡排序


冒泡排序通过两两比较相邻元素,如果反序,即大的在前面小的在后面时则交换,小的元素被交换至前面,一直到没有反序的情况为止,之所以叫做冒泡排序是因为在排序过程中,越小的元素会经过交换像一个个气泡一样浮到水面,即最小的元素会到数列的最前面,而最大的元素将会沉到数列的最底端。


冒泡排序中无论元素如何排列,给含有n个元素的列表进行冒泡排序需遍历n-1轮,该算法的时间复杂度是O(n2),其中最好情况下,列表已处于有序不需要执行交换操作;而最坏情况下,每一次比较都将导致一次交换。

冒泡排序的程序代码如下:

#冒泡排序
def BubbleSort(alist):
    for passnum in range(len(alist) - 1, 0, -1):  # range()函数中取列表的长度减一至0,每次循环-1
        for i in range(passnum):
            if alist[i] > alist[i + 1]:  # 若列表中下标为i的元素大于下标为i+1的元素,则执行以下操作交换两个变量
                temp = alist[i]  # 中间变量temp存储下标为i的元素
                alist[i] = alist[i + 1]  # 交换两个元素
                alist[i + 1] = temp  # 中间变量temp赋值给下标为i+1的元素
    print(alist)
BubbleSort([3, 0, 1, 9, 10, -2])

运行结果如下:

1667134174257.jpg

具体的执行步骤如下:

1667134611545.jpg

1、由于python的特性,通过同时赋值将交换的三行语句直接替换为一条语句:

temp = alist[i]
alist[i] = alist[i + 1]
alist[i + 1] = temp

替换为a,b=b,a的形式,从而直接交换:

alist[i], alist[i + 1] = alist[i + 1], alist[i]

def BubbleSort(alist):

 

for passnum in range(len(alist) - 1, 0, -1):  # range()函数中取列表的长度减一至0,每次循环-1
        for i in range(passnum):
            if alist[i] > alist[i + 1]:  # 若列表中下标为i的元素大于下标为i+1的元素,则执行以下操作交换两个变量
                alist[i], alist[i + 1] = alist[i + 1], alist[i]  # 直接使用一条语句进行交换
    print(alist)
BubbleSort([3, 0, 1, 9, 10, -2])


运行结果如下:

1667134651914.jpg

2、短冒泡

冒泡排序的效率很低,因为在确定最终的位置前必须交换元素,它遍历了列表中未排序的部分。

若在一轮遍历中没有产生交换,则可以说明列表中的元素已经有序,此时可以通过修改程序使程序终止,称为短冒泡排序,其中变量passnum控制循环的循环次数,从而在遍历中没有产生交换时终止,代码如下:

# 短冒泡排序
def ShortBubbleSort(alist):
    exchanges = True  # 变量exchanges值一开始为True
    passnum = len(alist) - 1  # 变量passnum值为列表长度减一
    while passnum > 0 and exchanges:
        exchanges = False
        for i in range(passnum):
            if alist[i] > alist[i + 1]:
                exchanges = True
                alist[i], alist[i + 1] = alist[i + 1], alist[i]
        passnum = passnum - 1
    print(alist)
ShortBubbleSort([3, 0, 1, 9, 10, -2])


运行结果如下:

1667134714349.jpg


(二)选择排序


选择排序每次遍历列表时只做一次交换,在每次遍历时寻找最大值,并在遍历完之后将它放到正确位置上。若给n个元素排序,需要遍历n-1轮,这是因为最后一个元素要到n-1轮遍历后才就位,该算法的时间复杂度也是O(n2)。

选择排序的代码如下:

# 选择排序
def SelectSort(alist):
    for i in range(len(alist) - 1, 0, -1):  # range()函数中取列表的长度减一至0,每次循环-1
        positionOfMax = 0
        for location in range(1, i + 1):  # 每次遍历列表时进行一次交换
            if alist[location] > alist[positionOfMax]:
                positionOfMax = location
        alist[i], alist[positionOfMax] = alist[positionOfMax], alist[i]
    print(alist)
SelectSort([9, 12, 0, -6, 7, 36, 1])


运行结果如下:

1667134895878.jpg

具体的执行步骤如下:

1667134907531.jpg

通过将冒泡排序和选择排序比较,排序列表[9, 12, 0, -6, 7, 36, 1],选择排序只交换了5次,而冒泡排序多达十几次,因此选择排序算法通常更快。


(三)插入排序


插入排序的原理是在列表的一端维护一个已有序的子列表,逐个将另一边的元素插入至这个有序的列表中,每轮将当前元素与有序子列表中的元素进行比较,有序子列表中将比它的元素右移,遇到比它小的或到达子列表终点时,就将当前元素插入,最后使整个列表有序化,插入排序的时间复杂度也是O(n2)。

插入排序的代码如下:

# 插入排序
def InsertSort(alist):
    for index in range(1, len(alist)):  # for循环次数为1至列表的长度-1
        currentvalue = alist[index]
        position = index
        while position > 0 and alist[position - 1] > currentvalue:
            alist[position] = alist[position - 1]
            position = position - 1
            alist[position] = currentvalue
        print(alist)
print(InsertSort([2, 0, 9, -3, 1, 4]))

运行结果如下:

1667134945642.jpg

具体的执行步骤如下,左边标红的数字为排列好的顺序:

1667134955745.jpg


(四)希尔排序


希尔排序是经插入排序改进后的排序算法,它将排序的列表切分成多个子列表,然后对每一个子列表进行插入排序,其中切分列表使用一个增量(或步长)选取间隔为一定增量的元素组成子列表,增量为sublistcount = len(alist) // 2,然后缩小增量以sublistcount = sublistcount // 2继续切分列表,最后再对此时增量为1的列表进行插入排序。

采用以下代码的希尔排序的时间复杂度是O(n1)至O(n2)之间,希尔排序的程序代码如下:

# 希尔排序
def ShellSort(alist):
    sublistcount = len(alist) // 2  # 选取增量为列表长度的2的整除,切分列表
    while sublistcount > 0:
        for startposition in range(sublistcount):
            InsertSort(alist, startposition, sublistcount)
        print("当前选取增量:", sublistcount, "当前列表顺序:", alist)
        sublistcount = sublistcount // 2  # 继续切分列表
def InsertSort(alist, start, gap): # 插入排序
    for i in range(start + gap, len(alist), gap):
        currentvalue = alist[i]
        position = i
        while position > 0 and alist[position - gap] > currentvalue:
            alist[position] = alist[position - gap]
            position = position - gap
            alist[position] = currentvalue
ShellSort([2, 0, 9, -3, 1, 4])


运行结果如下:

1667135228268.jpg

具体的执行步骤如下:

1667135237234.jpg


(五)归并排序


归并排序是一种递归算法,每次操作将当前列表一分为二进行拆分,若分开的子列表为空或只有一个元素时,则可以认为它是有序的;若子列表不为空则再将列表一分为二,对两部分都递归调用归并排序,依次进行下去,当对一分为二的两部分子列表都有序后,再将其归并为一个有序列表,即完成排序。

归并排序的时间复杂度是O(nlogn),但是归并过程需要用到额外的存储空间,归并排序的程序代码如下:

# 归并排序
def MergeSort(alist):
    print("切分", alist)
    if len(alist) > 1:  # 若列表的长度大于1则执行以下代码
        mid = len(alist) // 2  # 将当前列表一分为二进行拆分
        left_half = alist[:mid]  # 拆分后的的左子列表
        right_half = alist[mid:]  # 拆分后的的右子列表
        MergeSort(left_half)  # 递归调用自身函数
        MergeSort(right_half)  # 递归调用自身函数
        i, j, k = 0, 0, 0
        while i < len(left_half) and j < len(right_half):  # 将拆分的列表归并为一个有序列表
            if left_half[i] < right_half[j]:
                alist[k] = left_half[i]
                i = i + 1
            else:
                alist[k] = right_half[j]
                j = j + 1
            k = k + 1
        while i < len(left_half):
            alist[k] = left_half[i]
            i = i + 1
            k = k + 1
        while j < len(right_half):
            alist[k] = right_half[j]
            j = j + 1
            k = k + 1
    print("归并 ", alist)
MergeSort([2, 0, 9, -3, 4, 1])


运行结果如下:

1667135272637.jpg

具体的执行步骤如下:

1667135280984.jpg


(六)快速排序


和归并排序一样,快速排序也是采用一分为二的方式,但它不会占用额外的存储空间,不过列表可能不会被一分为二从而影响效率。

首先,快速排序会选出一个基准值用于切分列表,也就是分隔点;然后进行分区,将大于基准值和小于基准值的元素放在两边,其中leftmark和rightmark变量代表位于列表中剩余的所有元素的开头元素和末尾元素。

快速排序的程序代码如下:

# 快速排序
def quickSort(alist):
    quickSortHelper(alist, 0, len(alist) - 1)
    print(alist)
def quickSortHelper(alist, first, last):  # 递归函数
    if first < last:
        splitpoint = partition(alist, first, last)
        quickSortHelper(alist, first, splitpoint - 1)
        quickSortHelper(alist, splitpoint + 1, last)
def partition(alist, first, last):  # 分区函数
    pivotvalue = alist[first]
    leftmark = first + 1
    rightmark = last
    done = False
    while not done:
        while leftmark <= rightmark and alist[leftmark] <= pivotvalue:
            leftmark = leftmark + 1
        while alist[rightmark] >= pivotvalue and rightmark >= leftmark:
            rightmark = rightmark - 1
        if rightmark < leftmark:
            done = True
        else:
            alist[leftmark], alist[rightmark] = alist[rightmark], alist[leftmark]
    alist[first], alist[rightmark] = alist[rightmark], alist[first]
    return rightmark
quickSort([2, 7, -1, 0, 9, -3, 4, 1])


运行结果如下:

1667135328893.jpg

具体的执行步骤如下:

1667135340092.jpg


总结


名称 时间复杂度
顺序搜索 不论列表是否有序,都为O(n)
二分搜索 对于有序列表,为O(logn)
冒泡排序 O(n2)
选择排序 O(n2)
插入排序 O(n2)
希尔排序 介于O(n)和O(n2)之间
归并排序 O(nlogn)
快速排序 O(nlogn),当切分点不靠近列表中部时会降低至O(n2)


相关文章
|
15天前
|
Python
二分查找变种大赏!Python 中那些让你效率翻倍的搜索绝技!
二分查找是一种高效的搜索算法,适用于有序数组。其基本原理是通过不断比较中间元素来缩小搜索范围,从而快速找到目标值。常见的变种包括查找第一个等于目标值的元素、最后一个等于目标值的元素、第一个大于等于目标值的元素等。这些变种在实际应用中能够显著提高搜索效率,适用于各种复杂场景。
34 9
|
16天前
|
算法 数据处理 开发者
超越传统:Python二分查找的变种策略,让搜索效率再上新台阶!
本文介绍了二分查找及其几种Python实现的变种策略,包括经典二分查找、查找第一个等于给定值的元素、查找最后一个等于给定值的元素以及旋转有序数组的搜索。通过调整搜索条件和边界处理,这些变种策略能够适应更复杂的搜索场景,提升搜索效率和应用灵活性。
28 5
|
12天前
|
机器学习/深度学习 人工智能 算法
基于Python深度学习的【垃圾识别系统】实现~TensorFlow+人工智能+算法网络
垃圾识别分类系统。本系统采用Python作为主要编程语言,通过收集了5种常见的垃圾数据集('塑料', '玻璃', '纸张', '纸板', '金属'),然后基于TensorFlow搭建卷积神经网络算法模型,通过对图像数据集进行多轮迭代训练,最后得到一个识别精度较高的模型文件。然后使用Django搭建Web网页端可视化操作界面,实现用户在网页端上传一张垃圾图片识别其名称。
43 0
基于Python深度学习的【垃圾识别系统】实现~TensorFlow+人工智能+算法网络
|
12天前
|
机器学习/深度学习 人工智能 算法
【手写数字识别】Python+深度学习+机器学习+人工智能+TensorFlow+算法模型
手写数字识别系统,使用Python作为主要开发语言,基于深度学习TensorFlow框架,搭建卷积神经网络算法。并通过对数据集进行训练,最后得到一个识别精度较高的模型。并基于Flask框架,开发网页端操作平台,实现用户上传一张图片识别其名称。
42 0
【手写数字识别】Python+深度学习+机器学习+人工智能+TensorFlow+算法模型
|
12天前
|
机器学习/深度学习 人工智能 算法
基于深度学习的【蔬菜识别】系统实现~Python+人工智能+TensorFlow+算法模型
蔬菜识别系统,本系统使用Python作为主要编程语言,通过收集了8种常见的蔬菜图像数据集('土豆', '大白菜', '大葱', '莲藕', '菠菜', '西红柿', '韭菜', '黄瓜'),然后基于TensorFlow搭建卷积神经网络算法模型,通过多轮迭代训练最后得到一个识别精度较高的模型文件。在使用Django开发web网页端操作界面,实现用户上传一张蔬菜图片识别其名称。
52 0
基于深度学习的【蔬菜识别】系统实现~Python+人工智能+TensorFlow+算法模型
|
16天前
|
算法 Python
在Python编程中,分治法、贪心算法和动态规划是三种重要的算法。分治法通过将大问题分解为小问题,递归解决后合并结果
在Python编程中,分治法、贪心算法和动态规划是三种重要的算法。分治法通过将大问题分解为小问题,递归解决后合并结果;贪心算法在每一步选择局部最优解,追求全局最优;动态规划通过保存子问题的解,避免重复计算,确保全局最优。这三种算法各具特色,适用于不同类型的问题,合理选择能显著提升编程效率。
32 2
|
25天前
|
算法 测试技术 开发者
在Python开发中,性能优化和代码审查至关重要。性能优化通过改进代码结构和算法提高程序运行速度,减少资源消耗
在Python开发中,性能优化和代码审查至关重要。性能优化通过改进代码结构和算法提高程序运行速度,减少资源消耗;代码审查通过检查源代码发现潜在问题,提高代码质量和团队协作效率。本文介绍了一些实用的技巧和工具,帮助开发者提升开发效率。
28 3
|
28天前
|
机器学习/深度学习 人工智能 算法
【车辆车型识别】Python+卷积神经网络算法+深度学习+人工智能+TensorFlow+算法模型
车辆车型识别,使用Python作为主要编程语言,通过收集多种车辆车型图像数据集,然后基于TensorFlow搭建卷积网络算法模型,并对数据集进行训练,最后得到一个识别精度较高的模型文件。再基于Django搭建web网页端操作界面,实现用户上传一张车辆图片识别其类型。
72 0
【车辆车型识别】Python+卷积神经网络算法+深度学习+人工智能+TensorFlow+算法模型
|
存储 算法 搜索推荐
十大排序算法思想与 Python 实现(下)
一般排序算法最常考的:快速排序和归并排序。这两个算法体现了分治算法的核心观点,而且还有很多出题的可能。
十大排序算法思想与 Python 实现(下)
|
搜索推荐 算法 索引
十大排序算法思想与 Python 实现(上)
一般排序算法最常考的:快速排序和归并排序。这两个算法体现了分治算法的核心观点,而且还有很多出题的可能。
下一篇
无影云桌面