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)


相关文章
|
2天前
|
算法 数据可视化 Python
Python贝叶斯推断Metropolis-Hastings(M-H)MCMC采样算法的实现
Python贝叶斯推断Metropolis-Hastings(M-H)MCMC采样算法的实现
|
2天前
|
数据可视化 算法 数据挖掘
PYTHON实现谱聚类算法和改变聚类簇数结果可视化比较
PYTHON实现谱聚类算法和改变聚类簇数结果可视化比较
|
2天前
|
机器学习/深度学习 存储 算法
PYTHON集成机器学习:用ADABOOST、决策树、逻辑回归集成模型分类和回归和网格搜索超参数优化
PYTHON集成机器学习:用ADABOOST、决策树、逻辑回归集成模型分类和回归和网格搜索超参数优化
25 7
|
2天前
|
机器学习/深度学习
模型选择与调优:scikit-learn中的交叉验证与网格搜索
【4月更文挑战第17天】在机器学习中,模型选择和调优至关重要,scikit-learn提供了交叉验证和网格搜索工具。交叉验证(如k折、留一法和分层k折)用于评估模型性能和参数调优。网格搜索(如GridSearchCV和RandomizedSearchCV)遍历或随机选择参数组合以找到最优设置。通过实例展示了如何使用GridSearchCV对随机森林模型进行调优,强调了理解问题和数据的重要性。
|
2天前
|
算法 数据可视化 Python
Python中LARS和Lasso回归之最小角算法Lars分析波士顿住房数据实例
Python中LARS和Lasso回归之最小角算法Lars分析波士顿住房数据实例
11 0
|
2天前
|
Python
基于Django的Python应用—学习笔记—功能完善
基于Django的Python应用—学习笔记—功能完善
|
3天前
|
机器学习/深度学习 算法 Python
使用Python实现集成学习算法:Bagging与Boosting
使用Python实现集成学习算法:Bagging与Boosting
15 0
|
4天前
|
缓存 算法 Python
python算法对音频信号处理Sonification :Gauss-Seidel迭代算法
python算法对音频信号处理Sonification :Gauss-Seidel迭代算法
|
7天前
|
算法 数据可视化 数据挖掘
使用Python实现DBSCAN聚类算法
使用Python实现DBSCAN聚类算法
139 2
|
8天前
|
存储 算法 安全
Python加密算法有哪些?有什么作用?
这些加密算法的作用在于保护敏感数据的隐私和完整性。它们可以用于数据传输、存储、身份验证和数字签名等领域。通过加密,可以确保数据在传输和存储过程中不被未经授权的人访问或篡改。同时,数字签名可以用于验证数据的来源和完整性,防止数据被篡改或冒充。不同的加密算法在不同的应用场景中起到不同的作用,选择合适的算法取决于安全需求和性能要求。 买CN2云服务器,免备案服务器,高防服务器,就选蓝易云。百度搜索:蓝易云
6 0

热门文章

最新文章