当前位置: 首页 > news >正文

排序优化:如何实现一个通用的、高性能的排序函数?

文章来源于极客时间前google工程师−王争专栏。

几乎所有的编程语言都会提供排序函数,比如java中的Collections.sort()。在平时的开发中,我们都是直接使用,这些排序函数是如何实现的?底层都利用了哪种排序算法呢?

问题:如何实现一个通用的、高性能的排序函数?

如何选择合适的排序算法?

image

线性排序算法时间复杂度比较低,使用场景比较特殊。所以如果要写一个通用的排序函数,不能选择线性排序算法。

对于小规模数据进行排序,可以选择O(n^2)的算法;如果对大规模数据进行排序,O(nlogn)的算法更加高效。所以,为了兼顾任意规模数据的排序,一般都会首选时间复杂度为O(nlogn)的算法。

O(nlogn)的排序算法有归并排序、快速排序、还有堆排序。快排和堆排都有比较多的应用,比如java语言采用堆排序实现排序函数;c语言使用快排实现排序函数

快排比较适合来实现排序函数,但是快排在最坏情况下时间复杂度为O(n^2),如何来解决这个“复杂度恶化”的问题呢?

如何优化快速排序?

时间复杂度退化为O(n2)的原因是,数据原来就是有序的或者接近有序的,每次分区点都选择最后一个数据。**实际上,这种O(n2)时间复杂度出现的主要原因还是因为我们分区点选的不够合理。**

最理想的分区点是:被分区点分开的两个分区中,数据的数量差不多。

为了提高排序算法的性能,我们也要尽可能地让每次分区都比较平均。

比较常用、简单的分区算法:

1.三数取中法

从区间的首、尾、中间取出一个数,然后对比大小,取这3个数的中间值作为分区点。如果排序的数组比较大,那么“三数取中”可能就不够了,可能要“五数取中”或者“十数取中”。

2.随机法

从排序区间中随机选择一个元素作为分区点。

快排是用递归来实现的。递归要警惕堆栈溢出。

  • 限制递归深度,设定阈值,超过就停止递归。
  • 堆上模拟实现一个函数调用栈,手动模拟递归压栈、出栈过程,这样就没有了系统栈大小的限制。

举例分析排序函数

C语言中的qsort()函数。源码解析:

qsort()优先使用归并排序来排序输入数据,归并排序空间复杂度为O(n),对于小数据量的排序,比如1KB、2KB等,归并排序额外需要1KB、2KB的内存空间,问题不大。空间换时间思想。

如果数据量太大,比如100MB,归并排序就不合适了。所以,当数据量比较大的时候,qsort()会改用快速排序算法来排序。qsort()选择分区点的方法就是“三数取中法”

递归太深导致堆栈溢出的问题,qsort()通过自己实现一个堆上的栈,手动模拟递归来解决。

qsort()不仅仅用到了归并排序和快速排序,它还用了插入排序。排序过程中,当要排序的区间中,元素的个数小于等于4,qsort()就退化为插入排序,不再继续用递归来做快速排序。在小规模数据面前,O(n^2)时间复杂度的算法并不一定比O(nlogn)的算法执行时间长。

复杂度分析比较偏理论,深究的话,实际上时间复杂度并不等于代码实际的运行时间。

如果不省略低阶、系数和常数。O(nlogn) = O(knlogn+c)

假设K=1000,c=200,当我们对小规模数据(n=100)排序,n^2实际上比Knlogn+c还要小。

knlogn+c = 1000 * 100 * log100 + 200 远大于 10000n^2 = 100*100 = 10000

qsort()插入排序的算法实现中,使用哨兵编程技巧,虽然哨兵可能只是少做一次判断,但毕竟排序函数是非常常用、基础的函数,性能优化要做到极致。

总结

大部分排序函数都是采用O(nlogn)排序算法实现,但是为了尽可能提高性能,会做很多优化。

排序中的优化策略,比如合理选择分区点、避免递归太深等。

思考

学习Arrays.sort()源码

相关文章:

排序优化:如何实现一个通用的、高性能的排序函数?

文章来源于极客时间前google工程师−王争专栏。 几乎所有的编程语言都会提供排序函数,比如java中的Collections.sort()。在平时的开发中,我们都是直接使用,这些排序函数是如何实现的?底层都利用了哪种排序算法呢? 问题…...

车载开发学习——CAN总线

CAN总线又称为汽车总线,全程为“控制器局域网(Controller Area Network)”,即区域网络控制器,它将区域内的单一控制单元以某种形式连接在一起,形成一个系统。在这个系统内,大家以一种大家都认可…...

2023年知名国产数据库厂家汇总

随着信创国产化的崛起,大家纷纷在寻找可替代的国产数据库厂家。这里小编就给大家汇总了一些国内知名数据库厂家,仅供参考哦! 2023年知名国产数据库厂家汇总 1、人大金仓 2、瀚高 3、高斯 4、阿里云 5、华为云 6、浪潮 7、达梦 8、南大…...

【ARM Coresight SoC-400/SoC-600 专栏导读】

文章目录 1. ARM Coresight SoC-400/SoC-600 专栏导读目录1.1 Coresight 专题1.1.1 Performance Profiling1.1.2 ARM Coresight DS-5 系列 1. ARM Coresight SoC-400/SoC-600 专栏导读目录 本专栏全面介绍 ARM Coresight 系统 及SoC-400, SoC-600 中的各个组件。 1.1 Coresigh…...

在Go中创建自定义错误

引言 Go提供了两种在标准库中创建错误的方法,[errors.New和fmt.Errorf],当与用户交流更复杂的错误信息时,或在调试时与未来的自己交流时,有时这两种机制不足以充分捕获和报告所发生的情况。为了传达更复杂的错误信息并实现更多的…...

Vue.js2+Cesium1.103.0 十三、通过经纬度查询 GeoServer 发布的 wms 服务下的 feature 对象的相关信息

Vue.js2Cesium1.103.0 十三、通过经纬度查询 GeoServer 发布的 wms 服务下的 feature 对象的相关信息 Demo <template><divid"cesium-container"style"width: 100%; height: 100%;"><div style"position: absolute;z-index: 999;bott…...

使用STM32怎么喂狗 (IWDG)

STM32F1 的独立看门狗&#xff08;以下简称 IWDG&#xff09;。 STM32F1内部自带了两个看门狗&#xff0c;一个是独立看门狗 IWDG&#xff0c;另一个是窗口看门狗 WWDG&#xff0c; 本章只介绍独立看门狗 IWDG&#xff0c;窗口看门狗 WWDG 会在后面章节介绍。 本章要实现的功能…...

GEE:计算和打印GEE程序的执行时间

作者:CSDN @ _养乐多_ 本文记录了计算和打印程序的执行时间的Google Earth Engine (GEE)代码,并举例说明。 大家在执行GEE代码的时候,有时候为了对比两个不同的脚本,不知道代码执行花费了多少时间。本文记录了打印代码执行时间的函数,并举了一个应用案例说明。可以知道…...

GDPU 数据结构 天码行空5

一、实验目的 1&#xff0e;掌握队列的顺序存储结构 2&#xff0e;掌握队列先进先出运算原则在解决实际问题中的应用 二、实验内容 仿照教材顺序循环队列的例子&#xff0c;设计一个只使用队头指针和计数器的顺序循环队列抽象数据类型。其中操作包括&#xff1a;初始化、入队…...

SQLAlchemy学习-12.查询之 order_by 按desc 降序排序

前言 sqlalchemy的query默认是按id升序进行排序的&#xff0c;当我们需要按某个字段降序排序&#xff0c;就需要用到 order_by。 order_by 排序 默认情况下 sqlalchemy 的 query 默认是按 id 升序进行排序的 res session.query(Project).all() print(res) # [<Project…...

如何轻松打造数字人克隆系统+直播系统?OEM教你快速部署数字人SaaS系统源码

数字人做为国内目前最热门的人工智能创业赛道&#xff0c;连BAT都在跑步入局&#xff0c;中小企业更是渴望不渴及。但随着我国数字人头部品牌企业温州专帮信息科技有限公司旗下灰豚AI数字人平台的开源。使得中小企业零门槛可以轻松打造灰豚AI数字人一模一样的平台。灰豚数字人A…...

药物滥用第四篇介绍

OXY&#xff1a; 羟考酮&#xff08;Oxycodone&#xff0c;OXY&#xff09;&#xff0c;分子式为C18H21NO4&#xff0c;是一种半合成的蒂巴因衍生物。羟考酮为半合成的纯阿片受体激动药&#xff0c;其作用机制与吗啡相似&#xff0c;主要通过激动中枢神经系统内的阿片受体而起镇…...

Apache Doris (四十三): Doris数据更新与删除 - Update数据更新

🏡 个人主页:IT贫道_大数据OLAP体系技术栈,Apache Doris,Clickhouse 技术-CSDN博客 🚩 私聊博主:加入大数据技术讨论群聊,获取更多大数据资料。 🔔 博主个人B栈地址:豹哥教你大数据的个人空间-豹哥教你大数据个人主页-哔哩哔哩视频 目录 1. Update数据更新原理...

面试算法29:排序的循环链表

问题 在一个循环链表中节点的值递增排序&#xff0c;请设计一个算法在该循环链表中插入节点&#xff0c;并保证插入节点之后的循环链表仍然是排序的。 分析 首先分析在排序的循环链表中插入节点的规律。当在图4.15&#xff08;a&#xff09;的链表中插入值为4的节点时&…...

python中不可变类型和可变类型

不可变类型&#xff1a;修改之后内存存储地址不会发生改变 可变类型&#xff1a;修改之后内存存储地址发生改变 set...

vue3封装Axios库的 API 请求并使用拦截器来处理请求和响应

目录 为什么添加封装该部分&#xff1f; 具体代码&#xff1a; 对代码的解释&#xff1a; 如何使用&#xff1f; 为什么添加封装该部分&#xff1f; 简化发送 HTTP 请求的流程提供统一的错误处理机制支持用户状态管理和鉴权具备良好的扩展性和灵活性提高开发效率并使得代码…...

RK3588开发笔记(二):基于方案商提供sdk搭建引入mpp和sdk的宿主机交叉编译Qt5.12.10环境

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/133915614 红胖子网络科技博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬…...

rust学习——函数返回值

概念 Rust 中的函数定义以 fn 开始&#xff0c;后跟着函数名和一对圆括号。大括号告诉编译器函数体在哪里开始和结束。 特殊的地方——函数返回值 错误的写法 正解1 去掉分号 fn main() {let x plus_one(5);println!("The value of x is: {}", x); }fn plus_…...

【Cadence】配置文件cdsinit和cdsenv的使用

文件功能 .cdsinit文件&#xff1a;主要负责一些加载项的设置&#xff0c;一些脚本工具及一些快捷键 .cdsenv文件&#xff1a;主要负责一些环境变量或者参数的设置 文件位置&#xff1a; &#xff08;参照以下文件使用&#xff09; Virtuoso配置文件“.cdsenv”文件介绍和使…...

软考 系统架构设计师系列知识点之基于架构的软件开发方法ABSD(6)

接前一篇文章&#xff1a;软考 系统架构设计师系列知识点之基于架构的软件开发方法ABSD&#xff08;5&#xff09; 所属章节&#xff1a; 第7章. 系统架构设计基础知识 第5节. 特定领域软件体系结构 相关试题 1. 基于架构的软件设计&#xff08;ABSD&#xff09;强调由商业、…...

大数据零基础学习day1之环境准备和大数据初步理解

学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 &#xff08;1&#xff09;设置网关 打开VMware虚拟机&#xff0c;点击编辑…...

使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装

以下是基于 vant-ui&#xff08;适配 Vue2 版本 &#xff09;实现截图中照片上传预览、删除功能&#xff0c;并封装成可复用组件的完整代码&#xff0c;包含样式和逻辑实现&#xff0c;可直接在 Vue2 项目中使用&#xff1a; 1. 封装的图片上传组件 ImageUploader.vue <te…...

在Ubuntu中设置开机自动运行(sudo)指令的指南

在Ubuntu系统中&#xff0c;有时需要在系统启动时自动执行某些命令&#xff0c;特别是需要 sudo权限的指令。为了实现这一功能&#xff0c;可以使用多种方法&#xff0c;包括编写Systemd服务、配置 rc.local文件或使用 cron任务计划。本文将详细介绍这些方法&#xff0c;并提供…...

如何应对敏捷转型中的团队阻力

应对敏捷转型中的团队阻力需要明确沟通敏捷转型目的、提升团队参与感、提供充分的培训与支持、逐步推进敏捷实践、建立清晰的奖励和反馈机制。其中&#xff0c;明确沟通敏捷转型目的尤为关键&#xff0c;团队成员只有清晰理解转型背后的原因和利益&#xff0c;才能降低对变化的…...

WPF八大法则:告别模态窗口卡顿

⚙️ 核心问题&#xff1a;阻塞式模态窗口的缺陷 原始代码中ShowDialog()会阻塞UI线程&#xff0c;导致后续逻辑无法执行&#xff1a; var result modalWindow.ShowDialog(); // 线程阻塞 ProcessResult(result); // 必须等待窗口关闭根本问题&#xff1a…...

Pydantic + Function Calling的结合

1、Pydantic Pydantic 是一个 Python 库&#xff0c;用于数据验证和设置管理&#xff0c;通过 Python 类型注解强制执行数据类型。它广泛用于 API 开发&#xff08;如 FastAPI&#xff09;、配置管理和数据解析&#xff0c;核心功能包括&#xff1a; 数据验证&#xff1a;通过…...

React核心概念:State是什么?如何用useState管理组件自己的数据?

系列回顾&#xff1a; 在上一篇《React入门第一步》中&#xff0c;我们已经成功创建并运行了第一个React项目。我们学会了用Vite初始化项目&#xff0c;并修改了App.jsx组件&#xff0c;让页面显示出我们想要的文字。但是&#xff0c;那个页面是“死”的&#xff0c;它只是静态…...

当下AI智能硬件方案浅谈

背景&#xff1a; 现在大模型出来以后&#xff0c;打破了常规的机械式的对话&#xff0c;人机对话变得更聪明一点。 对话用到的技术主要是实时音视频&#xff0c;简称为RTC。下游硬件厂商一般都不会去自己开发音视频技术&#xff0c;开发自己的大模型。商用方案多见为字节、百…...

uniapp获取当前位置和经纬度信息

1.1. 获取当前位置和经纬度信息&#xff08;需要配置高的SDK&#xff09; 调用uni-app官方API中的uni.chooseLocation()&#xff0c;即打开地图选择位置。 <button click"getAddress">获取定位</button> const getAddress () > {uni.chooseLocatio…...

2025 后端自学UNIAPP【项目实战:旅游项目】7、景点详情页面【完结】

1、获取景点详情的请求【my_api.js】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口&#xff08;适配服务端返回 Token&#xff09; export const login async (code, avatar) > {const res await http(/login/getWXSessionKey, {code,avatar}); };//…...