蓝桥之手撕排序算法——冒泡、选择、插入、快排、归并(Python版)

2024-03-18 19:20

本文主要是介绍蓝桥之手撕排序算法——冒泡、选择、插入、快排、归并(Python版),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

1. 排序引言

2. 冒泡排序

2.1 算法思想

2.2 代码实现

 2.3 时空复杂度分析

3. 选择排序

3.1 算法思想

3.2 代码实现

 3.3 时空复杂度分析

4. 插入排序

4.1 算法思想

4.3 代码实现

4.4 时空复杂度分析

5. 快速排序

5.1 算法思想

5.2 代码实现

5.3 时空复杂度分析

6. 归并排序

6.1 算法思想

6.2 代码实现

6.3 时空复杂度分析


1. 排序引言

排序算法是算法竞赛中的第一入门必会的算法,可能在语言里面内置好sort排序函数,但是在排序算法中的很多思想是值得我们去学习的,比如从快速排序里面学会如何进行分治以及递归的实现。

2. 冒泡排序

冒泡排序是学习语言和算法中必会的一种算法,下面就由我来进行冒泡排序的分析与代码实现:

2.1 算法思想

对于一个无序数组,我们从索引0开始往右对比,如果当前数字比后一个数字大,就进行交换

这样每次就可以将最大的放在最右边, 上一次对比的最右边的就不再参与下一次排序

因为有N个数,每一次可以将一个最大数排好序,最后一个数也就定好了,因此只需要N-1次,就能排好完整的序。

我们可以看看以下的图:

其实到这里,冒泡排序算法就已经很明确了,每次冒泡都能求出当前最大的数,并将其放在最右边。

2.2 代码实现

a = [6, 5, 4, 1, 3, 2]
n = len(a)
for i in range(n - 1):for j in range(n - i - 1):if a[j] > a[j + 1]:a[j], a[j + 1] = a[j + 1], a[j]
print(a)

 2.3 时空复杂度分析

时间复杂度:

        每次需要比较进行n - i - 1次,也就是n - 1 、 n - 2 、 n - 3.....1次

        一共要执行n - 1次, 大概估算也就是O(n²)

空间复杂度:

        在原数组上面进行的操作,并没有开辟新的空间,所以为:O(1)

3. 选择排序

3.1 算法思想

每次从左往右开始找,找到最小的,然后与当前的索引的数进行交换,并索引加一

这样就能保证,每次都将最小的数排在最前面了。

3.2 代码实现

a = [6, 5, 4, 1, 3, 2]
n = len(a)
for i in range(n - 1):minn = a[i]index = ifor j in range(i + 1, n):if a[j] < minn:minn = min(minn, a[j])index = ja[i], a[index] = a[index], a[i]
print(a)

 3.3 时空复杂度分析

时间复杂度:

        每一次都要从左往右开始比较,从n - 1 次到1次,也就是n(n - 1) / 2次

        一共要进行n - 1次,所以时间复杂度为:O(n²)

空间复杂度:

        没有开辟额外空间,为:O(1)

4. 插入排序

4.1 算法思想

还是从左往右开始进行排序,当前这个数与前面的每一个数进行比较,如果当前的数比前一个数小,那么就一直往左边走,直到那个数比当前的数大为止。

到当前这个数的时候,前面的数其实已经排序好了,只需要找个合适的位置插入进行就好了。

4.3 代码实现

a = [6, 5, 4, 1, 3, 2 , 10]
n = len(a)
for i in range(1 , n):now = a[i]index = ifor j in range(i - 1 , -1 , -1):if a[j] > now:index = ja[j + 1] = a[j]else:breaka[index] = now
print(a)

4.4 时空复杂度分析

时间复杂度:

        插入排序比选择排序更加优化一点,但是最坏情况都是O(n²)

        但是最好的情况下(已经有序),只需要O(n)就行了

空间复杂度:

        没有开辟额外的数组,O(1)

5. 快速排序

5.1 算法思想

快速排序是基于分治算法实现的

分治:将一个大问题分解为多个小问题,分别解决这些小问题,然后将它们的解合并起来,从而得到大问题的解。通常,分治算法包含三个步骤:分解(Divide)、解决(Conquer)、合并(Combine)。

 要想理解分治,首先得理解什么是递归?

递归:递归是指在解决问题的过程中调用自身的过程。在编程中,递归是一种常见的编程技巧,它通过将问题分解成更小的、类似的子问题来解决复杂的问题。

 这是一个简单的递归函数:

def factorial(n):# 基本情况if n == 0:return 1# 递归情况return n * factorial(n - 1)

不难发现,其实这就是求解阶乘的函数,f(n) = n *  fn(n - 1),直到计算到最底层f(0) = 1 ,也就是0的阶乘,然后再不停地返回值,最终得到n的阶乘

当然,上面不理解的话,我们先可以看一下他的实现逻辑:

 先进行分解,算到最底层之后,又从下面往上面推,最终算出f(4)的结果为24

了解什么是分治和递归之后,我们就可以开始愉快的快速排序啦~

快速排序基本步骤:

  • 在数组中找一个基准值x, 一般是中间那个值
  • 将数组分成两个部分:1. 小于等于x的那部分, 2. 大于x的那部分
  • 对两边递归使用该策略

最重要的步骤其实是将数组分成两个部分:

  • 设置基准值l
  • 存放小于等于基准值的下标为:idx = l + 1
  • 从l + 1到r 遍历
    • 如果当前的a[i]<=l , a[i] , a[idx]互换,并且idx += 1
  • 最后就交换idx - 1和 l (idx是刚好大于l的,所以要-1,因为前面执行过一次idx += 1),就能保证l的左边是小于等于基准值的 , 右边是大于基准值的

5.2 代码实现

a = [6, 5, 4, 1, 3, 2, 10]
n = len(a)def fn(a, l, r):# 基准值为:lidx = l + 1   # 右边的索引for i in range(l + 1, r + 1):# 将小于基准值的方在左边 ,大于基准值的放在右边if a[i] <= a[l]:a[i], a[idx] = a[idx], a[i]idx += 1# 将基准值放在中间a[idx - 1], a[l] = a[l], a[idx - 1]# 返回基准值的位置return idx - 1def quick_sort(a, l, r):if l > r:returnmid = fn(a, l, r)  # 分基准值为l,分成两部分,左边<=mid , 右边>midquick_sort(a, l, mid - 1)   # 对左边处理quick_sort(a, mid + 1, r)   # 对右边处理quick_sort(a, 0, n - 1)
print(a)

5.3 时空复杂度分析

时间复杂度:

        在一般情况下,我们每次需要遍历分成两个部分,需要执行n次,每次都分成两个部分,相比线性时间,每次排序的都少了一半,于是就是Logn次

        总时间复杂度大概在O(n * logn)

空间复杂度:

        每次递归都是一次递归二叉树,消费的栈空间大概在O(logn)

6. 归并排序

6.1 算法思想

归并排序也是基于分治算法来的

只是归并排序是先递归,再进行合并

算法步骤:

  • 先分成两个部分
  • 每部分都处理成有序的
  • 再将两个数组合并起来

6.2 代码实现

a = [6, 5, 4, 1, 3, 2, 10]
n = len(a)def merge(a,b):res = []while len(a) != 0 and len(b)!=0:if a[0] <= b[0]:    # 将小的值先放入resres.append(a.pop(0))else:res.append(b.pop(0))# 将a,b剩下的值放进来res.extend(a)res.extend(b)return resdef merge_sort(a):if len(a) < 2:return amid  = len(a) // 2  # 每次分为两个部分left = merge_sort(a[:mid])   # 对左边处理right = merge_sort(a[mid:])   # 对右边处理return merge(left , right)   # 合并两部分a = merge_sort(a)
print(a)

6.3 时空复杂度分析

时间复杂度:

        归并排序与快速排序是类似的,都是O(n * logn)

空间复杂度:

        归并排序每次都需要开辟一个新空间,所以为O(n)

这篇关于蓝桥之手撕排序算法——冒泡、选择、插入、快排、归并(Python版)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/823384

相关文章

python常见环境管理工具超全解析

《python常见环境管理工具超全解析》在Python开发中,管理多个项目及其依赖项通常是一个挑战,下面:本文主要介绍python常见环境管理工具的相关资料,文中通过代码介绍的非常详细,需要的朋友... 目录1. conda2. pip3. uvuv 工具自动创建和管理环境的特点4. setup.py5.

Python常用命令提示符使用方法详解

《Python常用命令提示符使用方法详解》在学习python的过程中,我们需要用到命令提示符(CMD)进行环境的配置,:本文主要介绍Python常用命令提示符使用方法的相关资料,文中通过代码介绍的... 目录一、python环境基础命令【Windows】1、检查Python是否安装2、 查看Python的安

Python UV安装、升级、卸载详细步骤记录

《PythonUV安装、升级、卸载详细步骤记录》:本文主要介绍PythonUV安装、升级、卸载的详细步骤,uv是Astral推出的下一代Python包与项目管理器,主打单一可执行文件、极致性能... 目录安装检查升级设置自动补全卸载UV 命令总结 官方文档详见:https://docs.astral.sh/

Python并行处理实战之如何使用ProcessPoolExecutor加速计算

《Python并行处理实战之如何使用ProcessPoolExecutor加速计算》Python提供了多种并行处理的方式,其中concurrent.futures模块的ProcessPoolExecu... 目录简介完整代码示例代码解释1. 导入必要的模块2. 定义处理函数3. 主函数4. 生成数字列表5.

Python中help()和dir()函数的使用

《Python中help()和dir()函数的使用》我们经常需要查看某个对象(如模块、类、函数等)的属性和方法,Python提供了两个内置函数help()和dir(),它们可以帮助我们快速了解代... 目录1. 引言2. help() 函数2.1 作用2.2 使用方法2.3 示例(1) 查看内置函数的帮助(

Python虚拟环境与Conda使用指南分享

《Python虚拟环境与Conda使用指南分享》:本文主要介绍Python虚拟环境与Conda使用指南,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、python 虚拟环境概述1.1 什么是虚拟环境1.2 为什么需要虚拟环境二、Python 内置的虚拟环境工具

Python实例题之pygame开发打飞机游戏实例代码

《Python实例题之pygame开发打飞机游戏实例代码》对于python的学习者,能够写出一个飞机大战的程序代码,是不是感觉到非常的开心,:本文主要介绍Python实例题之pygame开发打飞机... 目录题目pygame-aircraft-game使用 Pygame 开发的打飞机游戏脚本代码解释初始化部

Python pip下载包及所有依赖到指定文件夹的步骤说明

《Pythonpip下载包及所有依赖到指定文件夹的步骤说明》为了方便开发和部署,我们常常需要将Python项目所依赖的第三方包导出到本地文件夹中,:本文主要介绍Pythonpip下载包及所有依... 目录步骤说明命令格式示例参数说明离线安装方法注意事项总结要使用pip下载包及其所有依赖到指定文件夹,请按照以

Python实现精准提取 PDF中的文本,表格与图片

《Python实现精准提取PDF中的文本,表格与图片》在实际的系统开发中,处理PDF文件不仅限于读取整页文本,还有提取文档中的表格数据,图片或特定区域的内容,下面我们来看看如何使用Python实... 目录安装 python 库提取 PDF 文本内容:获取整页文本与指定区域内容获取页面上的所有文本内容获取

基于Python实现一个Windows Tree命令工具

《基于Python实现一个WindowsTree命令工具》今天想要在Windows平台的CMD命令终端窗口中使用像Linux下的tree命令,打印一下目录结构层级树,然而还真有tree命令,但是发现... 目录引言实现代码使用说明可用选项示例用法功能特点添加到环境变量方法一:创建批处理文件并添加到PATH1