【排序算法】python之冒泡,选择,插入,快速,归并

参考资料:

  • 《Python实现5大排序算法》
  • 《六大排序算法:插入排序、希尔排序、选择排序、冒泡排序、堆排序、快速排序》 --代码似乎是C语言

————————
本文介绍5种常见的排序算法和基于Python实现:
冒泡排序(Bubble Sort):通过不断交换相邻元素,将较大的元素逐渐“冒泡”到数组的尾部,较小的元素逐渐沉到数组的头部。
选择排序(Selection Sort):每次从未排序的部分中选择最小(或最大)的元素,然后放到已排序部分的末尾(或开头)。
插入排序(Insertion Sort):将数组分为已排序和未排序两部分,逐个将未排序元素插入到已排序部分的适当位置,使其保持有序。
快速排序(Quick Sort):通过递归地选择一个基准元素,并将数组划分为小于基准和大于基准的两部分,然后对两部分继续进行快速排序。
归并排序(Merge Sort):将数组拆分成两个子数组,对每个子数组进行排序,然后再将两个有序子数组合并成一个有序数组。
不同的排序算法在时间复杂度、空间复杂度以及稳定性等方面有所不同,选择适合具体情况的排序算法可以提高排序效率。

一、冒泡排序

冒泡排序是一种简单的排序算法,它的思想是重复地走访过要排序的元素列,依次比较相邻的两个元素,如果它们的顺序错误就交换它们,直到没有任何再需要交换的元素,排序完成
在这里插入图片描述
案例
我们以升序为实例。【34,,64,,11,,12,,22,,25,,90】长度为7.
第一次循环,要判断交换n-1次=6.两两交换,要将最大值90沉到末尾。结果为:【34,,11,,12,,22,,25,,64,,90】
在这里插入图片描述

第二次循环,要判断n-2次=5.两两交换,要将次最大值64沉到尾部(90之前)。结果为:
【11,,12,,22,,25,,34,,64,,90】
第三次循环,要判断n-3次=4. 值得注意的是,在这次循环中,没有一次交换行为。
第四次循环,要判断n-4次=3.值得注意的是,在这次循环中,没有一次交换行为。
第五次循环,要判断n-5次=2.值得注意的是,在这次循环中,没有一次交换行为。
第六次循环,要判断n-6次=1.值得注意的是,在这次循环中,没有一次交换行为。

因此,设置一个“标志位”,用于优化。若某次循环中没有交换元素,说明数组已经有序,可以提前结束排序。即在第三次循环中,判断没有交换,则可以提前结束优化了。

1.1 代码

def bubble_sort(arr):n = len(arr)for i in range(n - 1):# 标志位,用于优化:若某次循环中没有交换元素,说明数组已经有序,可以提前结束排序swapped = Falsefor j in range(n - i - 1):if arr[j] > arr[j + 1]:arr[j], arr[j + 1] = arr[j + 1], arr[j]swapped = Trueif not swapped:break

测试

# 使用示例
arr = [64, 34, 25, 12, 22, 11, 90]
bubble_sort(arr)
print(arr)  # 输出:[11, 12, 22, 25, 34, 64, 90]

在冒泡排序中,外层循环控制需要比较的次数,内层循环实际进行相邻元素的比较和交换。通过标志位swapped的引入,可以优化冒泡排序,当某次循环没有发生交换时,说明数组已经有序,可以提前结束排序。这样在数组近乎有序的情况下,冒泡排序的性能会有所提升。

改进
“swapped”是优化的循环次数,提前截止循环。当序列的长度足够大的时候,在循环的某次的内部是否可以提前截止交换呢?
【22,,12,,11,25, 34,,64 ,90】

在这里插入图片描述
假设,如果我们用last_swap_index记录当前轮次最后交换的位置。在下一轮的循环的时候,提前结束判断交换。则在每个循环轮次中节省很多判断和步骤。因此,优化后的代码为:

1.2 优化后的代码

def bubble_sort(arr):n = len(arr)for i in range(n - 1):swapped = False# 记录当前轮循环最后一次交换的位置,初始值为未排序部分的末尾last_swap_index = n - 1for j in range(n - i - 1):if arr[j] > arr[j + 1]:arr[j], arr[j + 1] = arr[j + 1], arr[j]swapped = Truelast_swap_index = j  # 更新最后一次交换的位置if not swapped:break# 更新下一轮未排序部分的边界n = last_swap_index + 1

1.3 复杂度

时间复杂度:最坏情况:O(N^2)
      最好情况:O(N)
空间复杂度:O(1)

二、选择排序

选择排序(Selection Sort)是一种简单直观的排序算法,其思想是在未排序部分找到最小(或最大)元素,然后与未排序部分的第一个元素交换位置。重复这个过程直到所有元素都有序
在这里插入图片描述

2.1 代码

def selection_sort(arr):n = len(arr)for i in range(n - 1):# 假设当前未排序部分的第一个元素是最小的min_idx = i# 在未排序部分中找到最小元素的索引for j in range(i + 1, n):if arr[j] < arr[min_idx]:min_idx = j# 将找到的最小元素与未排序部分的第一个元素交换位置arr[i], arr[min_idx] = arr[min_idx], arr[i]

测试

arr = [64, 34, 25, 12, 22, 11, 90]
selection_sort(arr)
print(arr)  # 输出:[11, 12, 22, 25, 34, 64, 90]

在选择排序中,外层循环控制需要排序的次数,内层循环在未排序部分中找到最小元素的索引。然后将找到的最小元素与未排序部分的第一个元素交换位置。这样每一轮循环会将未排序部分的最小元素放到已排序部分的末尾,从而实现排序。选择排序的时间复杂度是O(n^2),它是一个不稳定的排序算法。
改进
换个角度看,外层循环指示了已排序部分的末尾,要求所有的元素都必须经历排序,因此,外层循环是不可能像冒泡排序一样提前截止的。在每次循环的内部选择最小元素的时候,我们也必须在所有未排序的元素中选择,也不可以提前截止。因此,我们只能将优化的注意力放在“交换”上。
例如:【11,,12,,22,,25,,34,,90,,64】
第一次循环,位置arr[0], 选择最小的arr[0]–>交换
第二次循环,位置arr[1], 选择最小的arr[1]–>交换
第二次循环,位置arr[2], 选择最小的arr[2]–>交换
。。。
可以看到,出现了很多不必要的交换。因此,添加一个判断因素,从而停止交换。—提醒的是,计算机做判断的消耗的运行时间远远小于数组的位置交换所消耗的时间。

2.2 优化的代码

def selection_sort(arr):n = len(arr)for i in range(n - 1):min_idx = ifor j in range(i + 1, n):if arr[j] < arr[min_idx]:min_idx = j# 如果最小元素的索引不是当前的第一个元素,才进行交换if min_idx != i:arr[i], arr[min_idx] = arr[min_idx], arr[i]

通过增加一个条件判断,只有当最小元素的索引不等于当前的第一个元素的索引时,才进行交换操作。这样就可以减少不必要的交换,进而优化选择排序的性能。这个优化对于已经近乎有序的数组尤为有效,因为这些情况下交换次数较少。尽管选择排序的时间复杂度仍然是O(n^2),但这样的优化可以提高它的效率。

2.3 复杂度

时间复杂度:最坏情况:O(N^2)
      最好情况:O(N^2)
空间复杂度:O(1)

三、插入排序

插入排序(Insertion Sort)是一种简单的排序算法,其基本思想是将数组分为已排序和未排序两个部分,逐个将未排序元素插入到已排序部分的适当位置,使其保持有序
在这里插入图片描述

在待排序的元素中,假设前n-1个元素已有序,现将第n个元素插入到前面已经排好的序列中,使得前n个元素有序。按照此法对所有元素进行插入,直到整个序列有序。
  但我们并不能确定待排元素中究竟哪一部分是有序的,所以我们一开始只能认为第一个元素是有序的,依次将其后面的元素插入到这个有序序列中来,直到整个序列有序为止。
在这里插入图片描述
案例
在这里插入图片描述

3.1 代码

def insertion_sort(arr):n = len(arr)for i in range(1, n):key = arr[i]  # 当前要插入的元素j = i - 1  # 已排序部分的最后一个元素的索引# 逐个将已排序部分中比key大的元素后移,为key找到合适的插入位置while j >= 0 and key < arr[j]:arr[j + 1] = arr[j]j -= 1# 插入key到合适的位置arr[j + 1] = key

在插入排序中,外层循环控制需要插入的次数,从第2个元素开始(索引为1),依次向已排序部分插入未排序部分的元素。内层循环用于将已排序部分中比当前元素key大的元素后移,为key找到合适的插入位置。最后,将key插入到合适的位置,完成一次插入操作

改进
在外层循环控制的插入次数,所有的元素都要被插入,因此无法提前截止。内层循环控制的是插入位置,一一倒序比较插入的位置是否合适。为了更快的找到插入位置,可以使用二分法判断插入位置。

3.2 优化的代码

def insertion_sort(arr):n = len(arr)for i in range(1, n):key = arr[i]left, right = 0, i - 1# 使用二分查找找到key应该插入的位置while left <= right:mid = (left + right) // 2if arr[mid] > key:right = mid - 1else:left = mid + 1# 将已排序部分中大于key的元素都后移一位for j in range(i, left, -1):arr[j] = arr[j - 1]arr[left] = key# 使用示例
arr = [64, 34, 25, 12, 22, 11, 90]
insertion_sort(arr)
print(arr)  # 输出:[11, 12, 22, 25, 34, 64, 90]

3.3 复杂度

时间复杂度

最坏情况下为O(N*N),此时待排序列为逆序,或者说接近逆序
最好情况下为O(N),此时待排序列为升序,或者说接近升序。

空间复杂度:O(1)

四、快速排序

快速排序(Quick Sort)是一种高效的排序算法,它使用分治法来对数组进行排序。快速排序的基本思想是选择一个基准元素(pivot),然后将数组中小于等于基准的元素放在左边,大于基准的元素放在右边,然后分别对左右两部分递归地进行快速排序,直到整个数组有序为止

案例
以【34,,25,,11,,22,,12,,64,,90】为例。

在这里插入图片描述

4.1 代码

def quick_sort(arr):if len(arr) <= 1:return arrpivot = arr[len(arr) // 2]  # 选择中间的元素作为基准left = [x for x in arr if x < pivot]middle = [x for x in arr if x == pivot]right = [x for x in arr if x > pivot]return quick_sort(left) + middle + quick_sort(right)

改进
在优化快速排序的代码中,主要关注减少递归调用的次数和减少额外空间的使用。以下是针对这两方面的优化方法:

  • 优化递归调用次数:对于小规模的子数组,可以切换到其他排序算法,例如插入排序。这是因为在小规模数据下,插入排序的常数项较小,比快速排序的递归开销更小。
  • 减少额外空间的使用:原始的快速排序需要额外的数组空间用于存放左右子数组,但我们可以通过就地分区的方式来减少空间使用。

或者说是用挖坑法 减少额外空间的使用。注意,在减少空间使用的的快速排序算法有很多,下面这个动态图比较形象,所以放在这里的。这与我们 下面使用的partition的代码不一致。

在这里插入图片描述

4.2 优化的代码

对于小规模的子数组,切换到插入排序

def insertion_sort(arr, low, high):for i in range(low + 1, high + 1):key = arr[i]j = i - 1while j >= low and key < arr[j]:arr[j + 1] = arr[j]j -= 1arr[j + 1] = keydef partition(arr, low, high):pivot = arr[high] ##注意这里的坑位与前面的动态图相反,选择的是最后一个位置而不是首位置i = low - 1for j in range(low, high):if arr[j] <= pivot:i += 1arr[i], arr[j] = arr[j], arr[i]arr[i + 1], arr[high] = arr[high], arr[i + 1]return i + 1def quick_sort(arr, low, high):if low < high:# 对于小规模的子数组,切换到插入排序if high - low + 1 <= 10:insertion_sort(arr, low, high)else:pivot_index = partition(arr, low, high)quick_sort(arr, low, pivot_index - 1)quick_sort(arr, pivot_index + 1, high)

测试

arr = [64, 34, 25, 12, 22, 11, 90]
quick_sort(arr, 0, len(arr) - 1)
print(arr)  # 输出:[11, 12, 22, 25, 34, 64, 90]

在优化后的代码中,我们引入了insertion_sort函数用于对小规模子数组进行插入排序。当子数组大小不超过10个元素时,切换到插入排序。同时,在quick_sort函数中采用就地分区的方法,减少了额外的空间使用。
这些优化方法可以提高快速排序在小规模数据和近乎有序数组的性能。但需要注意的是,优化的效果也依赖于具体的数据情况

4.3 案例讲解–partition的应用

以【72,,64,,34,,25,,12,,22,,90,,17,,45,,58】为例。当子数组规模小于3的时候,切换到“插入排序”。以其中一轮为例。
在这里插入图片描述

递归调用的树状图
在这里插入图片描述

五、 归并算法

归并排序(Merge Sort)是一种高效稳定的排序算法,它使用分治法将数组分为两个子数组,递归地对子数组进行排序,然后将排好序的子数组合并成一个有序数组。
在这里插入图片描述

5.1 代码

def merge_sort(arr):if len(arr) <= 1:return arr# 将数组一分为二mid = len(arr) // 2left = arr[:mid]right = arr[mid:]# 递归对左右两部分进行归并排序left = merge_sort(left)right = merge_sort(right)# 合并已排序的左右两部分return merge(left, right)def merge(left, right):merged = []left_idx, right_idx = 0, 0while left_idx < len(left) and right_idx < len(right):if left[left_idx] < right[right_idx]:merged.append(left[left_idx])left_idx += 1else:merged.append(right[right_idx])right_idx += 1# 将剩余的元素加入合并后的数组merged.extend(left[left_idx:])merged.extend(right[right_idx:])return merged

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/75505.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【机器学习】对 MLOps 的友好的介绍(MLOps1)

一、说明 我对 MLOps 感兴趣已经有一段时间了。我第一次从机器学习工程师那里了解到它&#xff0c;由于我当时还是一名博士生&#xff0c;我并不知道它的存在。然而&#xff0c;我的好奇心被激起了&#xff0c;我开始了解它。回想起来&#xff0c;我很后悔没有早点了解它&#…

iOS——锁与死锁问题

iOS中的锁 什么是锁锁的分类互斥锁1. synchronized2. NSLock3. pthread 递归锁1. NSRecursiveLock2. pthread 信号量Semaphore1. dispatch_semaphore_t2. pthread 条件锁1. NSCodition2. NSCoditionLock3. POSIX Conditions 分布式锁NSDistributedLock 读写锁1. dispatch_barri…

css实现卡片的左上角有一个三角形的遮盖效果

需求: 卡片的左上角有一个绿色的三角形标签,用来区分状态 实现: .vCard{position: relative;overflow: hidden; } .vCard::before {content: "";position: absolute;top: 0;left: 0;width: 0;height: 0;border-bottom: 20px solid transparent;border-left: 20px …

Ariadne’s Thread-使用文本提示改进对感染区域的分割胸部x线图像

论文&#xff1a;https://arxiv.org/abs/2307.03942&#xff0c; Miccai 2023 代码&#xff1a;GitHub - Junelin2333/LanGuideMedSeg-MICCAI2023: Pytorch code of MICCAI 2023 Paper-Ariadne’s Thread : Using Text Prompts to Improve Segmentation of Infected Areas fro…

2、Tomcat介绍(下)

组件分类 在Apache Tomcat中&#xff0c;有几个顶级组件&#xff0c;它们是Tomcat的核心组件&#xff0c;负责整个服务器的运行和管理。这些顶级组件包括&#xff1a; Server(服务器)&#xff1a;Tomcat的server.xml配置文件中的<Server>元素代表整个Tomcat服务器实例。每…

vmware网络配置

效果&#xff1a; 虚拟机和物理机网络互通&#xff1b; 虚拟机可以上外网 环境&#xff1a; vmware version 17.0.0 Centos 7.9 配置 1&#xff0c;vmware 菜单 - 编辑 - Virtual Network Edit 2&#xff0c; 选择VMnet8 VMnet information:NAT&#xff1b; 勾选2个…

运输层---概述

目录 运输层主要内容一.概述和传输层服务1.1 概述1.2 传输服务和协议1.3 传输层 vs. 网络层1.4 Internet传输层协议 二. 多路复用与多路分解&#xff08;解复用&#xff09;2.1 概述2.2 无连接与面向连接的多路分解&#xff08;解复用&#xff09;2.3面向连接的多路复用*2.4 We…

Html5播放器按钮在移动端变小的问题解决方法

Html5播放器按钮在移动端变小的问题解决方法 用手机浏览器打开酷播云视频&#xff0c;有时会出现播放器按钮太小的情况&#xff0c;此时只需在<head>中加入下面这段代码即可解决&#xff1a; <meta name"viewport" content"widthdevice-width, initia…

c语言指针的运算

1、通过指针计算数组的元素&#xff08;指针相减&#xff0c;类型需要一致&#xff09;&#xff0c;比如数组元素指针相减得到的是中间相差的元素个数&#xff0c;可以用于计算数组元素的个数等 #include "stdio.h" #include <stdlib.h>int main() {int a[10]…

SuperNova论文赏析

1. 引言 前序博客有&#xff1a; Nova: Recursive Zero-Knowledge Arguments from Folding Schemes学习笔记 卡内基梅隆大学 Abhiram Kothapalli 和 微软研究中心 Srinath Setty 2022年论文《SuperNova: Proving universal machine executions without universal circuits》…

责任链模式

责任链模式 1、定义&#xff1a; 将能够处理同一类请求的对象连成一条链&#xff0c;所提交的请求沿着链传递&#xff0c;链上的对象逐个判断是否有能力处理该请求 &#xff0c;如果能则处理&#xff1b; 如果不能则传递给链上的下一个对象 2、场景&#xff1a; 大学中奖学…

如何用python做自然语言处理

如何用python做自然语言处理 使用Python进行自然语言处理&#xff08;NLP&#xff09;是非常常见和强大的。以下是一些基本步骤&#xff1a; 安装所需的库&#xff1a; 首先&#xff0c;您需要安装一些用于自然语言处理的Python库&#xff0c;如NLTK&#xff08;自然语言工具包…

二十三种设计模式第十九篇--命令模式

命令模式是一种行为设计模式&#xff0c;它将请求封装成一个独立的对象&#xff0c;从而允许您以参数化的方式将客户端代码与具体实现解耦。在命令模式中&#xff0c;命令对象充当调用者和接收者之间的中介。这使您能够根据需要将请求排队、记录请求日志、撤销操作等。 命令模…

(具体解决方案)训练GAN深度学习的时候出现生成器loss一直上升但判别器loss趋于0

今天小陶在训练CGAN的时候出现了绷不住的情况&#xff0c;那就是G_loss&#xff08;生成器的loss值&#xff09;一路狂飙&#xff0c;一直上升到了6才逐渐平稳。而D_loss&#xff08;判别器的loss值&#xff09;却越来越小&#xff0c;具体的情况就看下面的图片吧。其实这在GAN…

arcgis字段计算器

1、两字段叠加。要求待叠加的字段类型为文本或字符串类型。如下&#xff1a; 2、字符串部分提取。

为Win12做准备?微软Win11 23H2将集成AI助手:GPT4免费用

微软日前确认今年4季度推出Win11 23H2&#xff0c;这是Win11第二个年度更新。 Win11 23H2具体有哪些功能升级&#xff0c;现在还不好说&#xff0c;但它会集成微软的Copilot&#xff0c;它很容易让人想到多年前的“曲别针”助手&#xff0c;但这次是AI技术加持的&#xff0c;Co…

编写SPI_Master驱动程序_新方法

编写SPI_Master驱动程序_新方法 文章目录 编写SPI_Master驱动程序_新方法一. SPI驱动框架1.1 总体框架1.2 怎么编写SPI_Master驱动1.2.1 编写设备树1.2.2 编写驱动程序 二、 编写程序2.1 数据传输流程2.2 写代码 致谢 参考资料&#xff1a; 内核头文件&#xff1a;include\lin…

Rookit系列一 【隐藏网络端口】【支持Win7 x32/x64 ~ Win10 x32/x64】

文章目录 Rookit系列一 【隐藏网络端口】【支持Win7 x32/x64 ~ Win10 x32/x64】前言探究隐藏网络端口netstat分析隐藏网络端口的原理关键数据结构隐藏网络端口源码 效果演示 Rookit系列一 【隐藏网络端口】【支持Win7 x32/x64 ~ Win10 x32/x64】 前言 Rookit是个老生常谈的话…

python中*与**的使用

文章目录 前言一、*与**在函数定义时二、*与**在函数调用时 前言 在python中*与**的使用要区分是在函数定义时还是在函数调用时。 一、*与**在函数定义时 def deng(*args,**kwargs):print(args)print(kwargs)deng(1,2,3,a 4,b 5)在函数定义时参数前面使用*&#xff0c;代表…

vue echart3个饼图

概览&#xff1a;根据UI设计需要做3个饼图且之间有关联&#xff0c;并且处理后端返回的数据。 参考链接&#xff1a; echart 官网的一个案例&#xff0c;3个饼图 实现思路&#xff1a; 根据案例&#xff0c;把数据处理成对应的。 参考代码&#xff1a; 1.处理后端数据&am…