十大排序算法详解-上篇:比较排序算法【python 动态图解】

news2024/11/16 11:48:13

作者介绍:10年大厂数据\经营分析经验,现任大厂数据部门负责人。
会一些的技术:数据分析、算法、SQL、大数据相关、python
欢迎加入社区:码上找工作
作者专栏每日更新:
LeetCode解锁1000题: 打怪升级之旅
python数据分析可视化:企业实战案例

1. 引言

在数据驱动的时代,排序算法无处不在,它们是计算机科学的基石之一。无论是在大数据分析、数据库管理、搜索引擎优化,还是在日常软件开发中,有效的排序都是提高效率和性能的关键。但是,排序不仅仅是将数据元素排列成有序序列那么简单,它是一种基础而强大的数据操作,影响着数据结构的选择和算法设计的整体策略。

为什么排序重要?

排序问题的重要性主要体现在以下几个方面:

  1. 数据检索:在排序的数据集上进行搜索比在未排序的数据集上更高效(比如,二分搜索法的前提是数据已排序)。
  2. 数据结构优化:许多数据结构(如优先队列、搜索树等)在内部使用排序机制来提高各种数据操作的效率。
  3. 信息可视化:在数据分析和科学计算中,排序是数据预处理的重要步骤,有助于识别趋势、异常和模式。
  4. 算法优化:许多更复杂的算法(如集合操作或数据库联接操作)的性能可以通过先对数据进行排序来显著提升。

排序的实际应用

实际应用中,排序算法的选择可能会根据具体情况而有很大差异。例如:

  • 在实时系统中,如交易系统,我们可能更倾向于使用时间复杂度最优的排序算法来保证快速响应。
  • 在处理极大数据集的分布式系统中,如使用Hadoop或Spark的环境,排序算法必须能有效地分布在多个节点上处理。
  • 在有严格内存限制的嵌入式系统中,空间效率也许是选择排序算法的决定性因素。

通过探索各种排序算法的性能特点和适用场景,我们不仅可以对它们的工作原理有一个系统的了解,还可以根据实际需要选择或者设计出最适合的算法。

2. 排序算法的分类

  • 比较类排序:基于比较元素之间的大小关系来进行排序。
  • 非比较类排序:不通过比较来决定元素间的顺序。

3. 常见的排序算法详解

					算法思维导图概览

算法思维导图概览

3.1 冒泡排序 (Bubble Sort)
①工作原理
  1. 遍历列表:从列表的第一个元素开始,比较相邻的两个元素。
  2. 比较和交换:如果一对元素是逆序的(即,左边的元素比右边的元素大),则交换它们的位置。
  3. 重复步骤:遍历整个列表,对每一对相邻元素执行步骤2,重复此过程,每次循环结束时,最大的元素会被放置在其最终位置上。
  4. 终止条件:当遍历列表时没有进行任何交换时,说明列表已经完全排序,此时算法结束。
②案例分析 力扣2

对输入的数组进行冒泡排序,输出排序后的数组

​输入:[74,55,35,79,57,71,81,5,82,1]

输出:[1,5,35,55,57,71,74,79,81,82]

内循环(比较与交换):算法从数组的第一个元素开始,比较相邻的元素对 (j, j+1)。如果 j 位置的元素大于 j+1 位置的元素(对于升序排序),则这两个元素的位置会被交换。这一过程一直重复,直到到达数组的末尾。每完成一轮内循环,都能保证这一轮中最大的元素被"冒泡"到其最终位置(即数组的最右端)。

要注意的优化:防止已经排序的重复执行,通过增加一个标志位 flag ,若在某轮「内循环」中未执行任何交换操作,则说明数组已经完成排序,直接返回结果即可。这个在已经排序好的情况下 可以减少不必要的比较

外循环(迭代排序的过程):外循环控制内循环的重复执行,每执行完一次内循环后,排序的范围就减少一个元素(因为每次内循环都会将当前未排序部分的最大元素放到正确的位置)。外循环持续进行,直到整个数组排序完成。
在这里插入图片描述
​​​​​动态图
在这里插入图片描述

③代码示例
def bubble_sort(arr):
    n = len(arr)
    for i in range(n):
        # 标记变量,用于优化检测是否有元素交换
        swapped = False
        # 最后的元素已经放置好了,每次迭代可以减少一次
        for j in range(0, n-i-1):
            # 从头到尾进行比较,不断交换直到最大的数“冒泡”到最后
            if arr[j] > arr[j+1]:
                arr[j], arr[j+1] = arr[j+1], arr[j]
                swapped = True
        # 如果在某次遍历中没有数据交换,表示已经完成排序,可以提前退出
        if not swapped:
            break

使用冒泡排序算法的示例代码体现了算法的直接性和简洁性,但在处理大数据集时,更高效的算法通常是更好的选择。

④算法分析

时间复杂度

  • 最好情况复杂度:(O(n))。当列表已经完全排序时,只需要进行一次遍历,如果没有发生交换,则排序完成。
  • 平均情况复杂度:(O(n^2))。每个元素都需要与其余的( n-1 )个元素比较,并可能需要交换。
  • 最坏情况复杂度:(O(n^2))。当列表完全逆序时,每个元素都需进行( n-1 )次比较和交换。

空间复杂度

  • 空间复杂度:(O(1))。冒泡排序是原地排序算法,除了原始列表,只需要常数级别的额外空间。
3.2 快速排序 (Quick Sort)

快速排序是由英国计算机科学家托尼·霍尔在1960年代提出的一种高效的排序算法。它使用分治策略来把一个序列分为两个子序列,具有较小的元素和较大的元素。

①工作原理
  1. 选择基准值:在数据集中,选择一个元素作为“基准”(pivot)。
  2. 分区操作:重新排列数据,所有比基准值小的元素摆放在基准前面,所有比基准值大的元素摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
  3. 递归排序:递归地将小于基准值元素的子序列和大于基准值元素的子序列排序。

快速排序的效率在于它可以在划分数组的同时进行排序。

②案例分析 力扣912

给你一个整数数组 nums,请你将该数组升序排列。

示例 1:

输入:nums = [5,2,3,1]
输出:[1,2,3,5]

示例 2:

输入:nums = [5,1,1,2,0,0]
输出:[0,0,1,1,2,5]

在这里插入图片描述

③代码示例
class Solution:
    def sortArray(self, nums):
        """
        主函数,调用快速排序函数对数组进行排序
        :param nums: List[int] 需要排序的整数数组
        :return: List[int] 排序后的数组
        """
        def quickSort(low, high):
            """
            快速排序的递归函数
            :param low: int 数组的起始索引
            :param high: int 数组的结束索引
            """
            if low < high:
                pi = partition(low, high)
                quickSort(low, pi - 1)
                quickSort(pi + 1, high)

        def partition(low, high):
            """
            对数组进行分区,返回基准点索引
            :param low: int 分区的起始索引
            :param high: int 分区的结束索引
            :return: int 基准点的索引
            """
            pivot = nums[high]  # 选取最后一个元素作为基准
            i = low - 1  # 小于基准的元素的索引
            for j in range(low, high):
                if nums[j] < pivot:
                    i += 1
                    nums[i], nums[j] = nums[j], nums[i]  # 交换元素
            nums[i+1], nums[high] = nums[high], nums[i+1]  # 将基准元素放到正确位置
            return i + 1

        quickSort(0, len(nums) - 1)  # 从整个数组的范围开始排序
        return nums

快速排序因其优异的平均性能和简单的实现成为了排序算法的首选,尤其是在处理大型数据集时。

④算法分析
  • 时间复杂度
    • 最好情况:(O(n \log n)),通常的情况下是所有排序算法中最快的。
    • 平均情况:(O(n \log n))。
    • 最坏情况:(O(n^2)),当数据已经是正序或者逆序时。
  • 空间复杂度
    • (O(\log n)),主要是递归造成的栈空间的使用。
3.3 归并排序 (Merge Sort)
①工作原理
  1. 分解:递归地把当前序列平均分割成两半。
  2. 解决:递归地解决每个子序列。
  3. 合并:将两个排序好的子序列合并成一个最终的排序序列。
②案例分析 力扣912

继续用力扣(LeetCode)上“912. 排序数组”问题可以使用归并排序解决。由于归并排序效率高并且稳定,特别适用于大数据集排序。
在这里插入图片描述

③代码示例
class Solution:
    def sortArray(self, nums: List[int]) -> List[int]:
        # 如果数组长度大于1,则继续分解
        if len(nums) > 1:
            # 找到中间索引,进行分割
            mid = len(nums) // 2
            # 分割成两个子数组
            L = nums[:mid]
            R = nums[mid:]

            # 递归排序两个子数组
            self.sortArray(L)
            self.sortArray(R)

            i = j = k = 0

            # 合并两个有序子数组
            while i < len(L) and j < len(R):
                if L[i] < R[j]:
                    nums[k] = L[i]
                    i += 1
                else:
                    nums[k] = R[j]
                    j += 1
                k += 1

            # 将剩余的元素复制到原数组中
            while i < len(L):
                nums[k] = L[i]
                i += 1
                k += 1
            while j < len(R):
                nums[k] = R[j]
                j += 1
                k += 1

        # 返回排序后的数组
        return nums
④算法分析
  • 时间复杂度:归并排序在最好、最坏和平均情况下都具有 (O(n \log n)) 的时间复杂度。
  • 空间复杂度:由于需要与原数组同等长度的存储空间来存储合并后的数组,所以空间复杂度为 (O(n))。
  • 稳定性:归并排序是一种稳定的排序算法,因为合并操作不会改变相同元素之间的相对顺序。

归并排序尤其适合用于链表类型的数据结构,或者大型数据集合中,因为它能够提供稳定且一致的性能。

3.4 堆排序 (Heap Sort)

堆排序是基于堆数据结构的一种比较排序算法。堆是一种近似完全二叉树的结构,且满足堆积性质:即任意节点的值总是不大于(或不小于)其子节点的值。

①工作原理
  1. 建立堆:将给定无序数组构造成一个最大堆(或最小堆)。
  2. 交换元素:将堆顶元素(最大值或最小值)与数组末尾元素交换,并将堆的有效大小减一。
  3. 恢复堆:将新的未排序的堆顶元素调整到合适位置,以重新满足堆的性质。
  4. 重复步骤:重复步骤2和3,直到堆的有效大小为1,此时数组已经排序完成。
②力扣案例分析

在力扣(LeetCode)上,题号为“215. 数组中的第K个最大元素”可以通过堆排序的方式来解决。堆排序非常适合用于解决此类问题,因为它可以在O(N log N)的时间内排序,同时可以在O(N)时间内构建堆,而且堆结构使得它能以O(log N)时间找到最大或最小值。
在这里插入图片描述

③代码示例
class Solution:
    def sortArray(self, nums: List[int]) -> List[int]:
        # 建立最大堆
        def heapify(arr, n, i):
            largest = i
            l = 2 * i + 1
            r = 2 * i + 2
            if l < n and arr[l] > arr[largest]:
                largest = l
            if r < n and arr[r] > arr[largest]:
                largest = r
            if largest != i:
                arr[i], arr[largest] = arr[largest], arr[i]
                heapify(arr, n, largest)

        # 主函数,调用堆排序
        n = len(nums)
        
        # 建立堆
        for i in range(n // 2 - 1, -1, -1):
            heapify(nums, n, i)
        
        # 一个个交换元素
        for i in range(n-1, 0, -1):
            nums[i], nums[0] = nums[0], nums[i]
            heapify(nums, i, 0)
        return nums
④算法分析
  • 时间复杂度:堆排序的时间复杂度为O(N log N),其中N是数组的长度。这是因为建立堆的过程是O(N),而进行N次调整的过程是O(N log N)。
  • 空间复杂度:堆排序是原地排序,不需要额外的存储空间,所以空间复杂度为O(1)。
  • 稳定性:堆排序是不稳定的排序算法,因为在调整堆的过程中,无法保证相同元素的相对顺序不变。

附件

部分动态图片来自:https://github.com/hustcc/JS-Sorting-Algorithm

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何通过cURL库实现远程控制插座

如何通过cURL库实现远程控制插座呢&#xff1f; 本文描述了使用cURL库调用HTTP接口&#xff0c;实现控制插座&#xff0c;即插即用&#xff0c;先插入插座&#xff0c;再接电器&#xff0c;实现远程控制。 可选用产品&#xff1a;可根据实际场景需求&#xff0c;选择对应的规格…

libtorrent - 安装小记

文章目录 官方文档&#xff1a;libtorrent python binding http://libtorrent.org/python_binding.html 1、下载代码 建议使用&#xff1a; git clone --recurse-submodules https://github.com/arvidn/libtorrent.git如果在 github web 界面下载代码&#xff0c;build 的时候…

进程动静态库

文章目录 动态库和静态库1. 静态库2. 动态库 承接上文&#xff1a; 文件描述符 动态库和静态库 静态库与动态库&#xff1a; 静态库&#xff08;.a&#xff09;&#xff1a;程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库动态库&#xf…

ISP比普通的静态代理相比有什么优势?

ISP&#xff08;Internet Service Provider&#xff09;&#xff0c;即互联网服务提供商&#xff0c;是向广大用户综合提供互联网接入业务、信息业务、增值业务的电信运营商。而静态代理则是一个固定不变的代理IP地址&#xff0c;具有稳定性强、兼容性好和管理方便等特点。当我…

上位机图像处理和嵌入式模块部署(树莓派4b之自动化测试)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 硬件、软件功能开发ok只是产品开发的第一步。怎么做到自动化测试、保证产品质量才是关键。很多时候&#xff0c;我们给客户提供了功能&#xff0c;…

适用于集成温度补偿晶体振荡器SG3225EEN

在现代电子系统中&#xff0c;随着技术的发展&#xff0c;对晶体振荡器的要求越来越高。例如&#xff0c;人工智能、5G等技术的应用需要更高的频率稳定度和更低的相位噪声&#xff0c;以确保数据传输的准确性和系统的高效运行。此外&#xff0c;随着电子设备向智能化、小型化发…

PHP+MYSQL多条件选一通用搜索系统功能单文件7KB

通用功能: 快速填写参数用于自己的mysql数据表搜索,ajax载入数据 <?php header("content-Type: text/html; charsetUTF-8"); //error_reporting(0);$dbhost "localhost"; //数据库地址本地localhost $dbuser "chalidecom"; //数据库账号 …

C语言扫雷游戏完整实现(下)

文章目录 前言一、排雷函数菜单二、排雷函数菜单的实现三、拓展棋盘功能四、源码1. test.c源文件2. game.h头文件3. game.c源文件 总结 前言 C语言实现扫雷游戏的排雷菜单&#xff0c;以及功能的实现&#xff0c;拓展棋盘功能&#xff0c;以及源码等。 上半部分的链接地址: C语…

第一篇【传奇开心果系列】Python深度学习库技术点案例示例:深度解读深度学习在自动驾驶领域的应用

传奇开心果博文系列 系列博文目录Python深度学习库技术点案例示例系列 博文目录前言一、深度学习在自动驾驶方面的应用介绍二、目标检测和识别示例代码三、路况感知示例代码四、行为预测示例代码五、路径规划示例代码六、自动驾驶控制示例代码七、感知融合示例代码八、高精度地…

PyCharm开发工具安装plugins插件

一. 简介 通过前面的学习&#xff0c;我们知道 python开发常用的一个开发工具&#xff08;即IDE&#xff09;是 PyCharm。 本文来简单介绍一下&#xff0c;PyCharm开发工具是如何安装 plugins插件的。其实与 vscode软件安装插件类似。 本文来学习 PyCharm开发工具安装一个中…

51.HarmonyOS鸿蒙系统 App(ArkUI)通知

普通文本通知测试 长文本通知测试 多行文本通知测试 图片通知测试 进度条通知测试 通知简介 应用可以通过通知接口发送通知消息&#xff0c;终端用户可以通过通知栏查看通知内容&#xff0c;也可以点击通知来打开应用。 通知常见的使用场景&#xff1a; 显示接收到的短消息、…

正则表达式.java

正则表达式的作用&#xff1a; ①可以校验字符串是否满足一定的规则&#xff0c;并用来校验数据格式的合法性&#x1f9f8; &#x1f9e9;[]:只能是括号里的字符 &#x1f9e9;[^]&#xff1a;除了括号里的字符 &#x1f9e9;[- -]:表示两段范围&#xff0c;满足其一即可 &a…

openstack-镜像封装 7

再克隆两台主机并且安装图形化组件和虚拟化组件 进入图形化界面并安装一个虚拟化管理器 根下创建一个目录&#xff0c;虚拟化管理器新添加一个路径 创建虚拟化 配置虚拟化主机 设置虚拟化主机配置 安装所需软件 清理创建云主机时安装的组件 主机安装虚拟化工具 清理虚拟化缓存 …

应用在防蓝光显示器中的LED防蓝光灯珠

相比抗蓝光眼镜、防蓝光覆膜、软体降低蓝光强度这些“软”净蓝手段&#xff0c;通过对LED的发光磷粉进行LED背光进行技术革新&#xff0c;可实现硬件“净蓝”。其能够将90%以上的有害蓝光转换为450nm以上的长波低能光线&#xff0c;从硬件的角度解决了蓝光危害眼睛的问题&#…

深入探究音视频开源库WebRTC中NetEQ音频抗网络延时与抗丢包的实现机制

目录 1、引言 2、WebRTC简介 3、什么是NetEQ&#xff1f; 4、NetEQ技术详解 4.1、NetEQ概述 4.2、抖动消除技术 4.3、丢包补偿技术 4.4、NetEQ概要设计 4.5、NetEQ的命令机制 4.6、NetEQ的播放机制 4.7、MCU的控制机制 4.8、DSP的算法处理 4.9、DSP算法的模拟测试…

基于Flask的岗位就业可视化系统(三)

前言 本项目综合了基本数据分析的流程&#xff0c;包括数据采集&#xff08;爬虫&#xff09;、数据清洗、数据存储、数据前后端可视化等 推荐阅读顺序为&#xff1a;数据采集——>数据清洗——>数据库存储——>基于Flask的前后端交互&#xff0c;有问题的话可以留言…

unity学习(87)——断线的原因--客户端堆栈溢出1

无论unity还是exe问题都是一样的&#xff0c;都是客户端一直在发123&#xff0c;但收不到124&#xff0c;退出时服务器能发126&#xff0c;但是客户端压根接受不到126。一下确实解决不了问题&#xff0c;但其实已经十分接近了&#xff01; 客户端断线后就再也收不到任何包了&a…

ElasticSearch批处理

在刚才的新增当中&#xff0c;我们是一次新增一条数据。那么如果你将来的数据库里有数千上万的数据&#xff0c;你一次新增一个&#xff0c;那得多麻烦。所以我们还要学习一下批量导入功能。 也就是说批量的把数据库的数据写入索引库。那这里的需求是&#xff0c;首先利用mybat…

ctfshow web41-web50

web41 代码审计 <?php if(isset($_POST[c])){$c $_POST[c]; if(!preg_match(/[0-9]|[a-z]|\^|\|\~|\$|\[|\]|\{|\}|\&|\-/i, $c)){eval("echo($c);");} }else{highlight_file(__FILE__); } ?> 过滤了&#xff1a;[0-9] [a-z] ^ ~ $ [ ] { } & -…

用户中心 -- 代码理解

一、删除表 & if 删除表 1.1 DROP TABLE IF EXISTS user 和 DROP TABLE user 网址&#xff1a; 用户管理第2节课 -- idea 2023.2 创建表--【本人】-CSDN博客 二、 代码 2.1 清空表中数据 的 命令 【truncate 清空】 网址&#xff1a; 用户管理第2节课 -- idea 2…