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

JavaScript排序算法详解:从基础到高级

排序是编程中最基本也是最重要的操作之一。JavaScript作为一门广泛应用于Web开发的语言,提供了内置的排序方法,但了解各种排序算法的原理和实现对于开发者来说仍然至关重要。本文将深入探讨JavaScript中常见的排序算法,帮助您理解它们的原理、实现方式以及适用场景。

一、JavaScript内置排序方法

在深入探讨各种算法之前,我们先看一下JavaScript提供的原生排序方法:

const arr = [3, 1, 4, 1, 5, 9, 2, 6];
arr.sort(); // 默认按Unicode码点排序,结果为 [1, 1, 2, 3, 4, 5, 6, 9]
arr.sort((a, b) => a - b); // 数字升序排序
arr.sort((a, b) => b - a); // 数字降序排序

虽然内置的sort()方法很方便,但了解其背后的原理和各种排序算法的特性对于解决复杂问题至关重要。

二、基本排序算法

1. 冒泡排序(Bubble Sort)

原理:重复遍历要排序的列表,比较相邻元素并交换它们的位置,直到列表排序完成。

function bubbleSort(arr) {let len = arr.length;for (let i = 0; i < len - 1; i++) {for (let j = 0; j < len - 1 - i; j++) {if (arr[j] > arr[j + 1]) {[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]; // ES6解构赋值交换}}}return arr;
}

时间复杂度

  • 最好情况:O(n)(已经排序的情况下)

  • 平均和最坏情况:O(n²)

适用场景:小数据集或基本有序的数据

2. 选择排序(Selection Sort)

原理:每次从未排序的部分选择最小(或最大)元素放到已排序部分的末尾。

function selectionSort(arr) {let len = arr.length;for (let i = 0; i < len - 1; i++) {let minIndex = i;for (let j = i + 1; j < len; j++) {if (arr[j] < arr[minIndex]) {minIndex = j;}}if (minIndex !== i) {[arr[i], arr[minIndex]] = [arr[minIndex], arr[i]];}}return arr;
}

时间复杂度:始终为O(n²)

特点:交换次数少,适合交换成本高的场景

3. 插入排序(Insertion Sort)

原理:构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。

function insertionSort(arr) {let len = arr.length;for (let i = 1; i < len; i++) {let current = arr[i];let j = i - 1;while (j >= 0 && arr[j] > current) {arr[j + 1] = arr[j];j--;}arr[j + 1] = current;}return arr;
}

时间复杂度

  • 最好情况:O(n)(已经排序的情况下)

  • 平均和最坏情况:O(n²)

适用场景:小数据集或基本有序的数据,比冒泡和选择排序更高效

三、高效排序算法

1. 快速排序(Quick Sort)

原理:分治法策略,选择一个基准元素,将数组分为两部分,一部分小于基准,一部分大于基准,然后递归地对这两部分进行排序。

function quickSort(arr) {if (arr.length <= 1) return arr;const pivot = arr[Math.floor(arr.length / 2)];const left = [];const right = [];const equal = [];for (let element of arr) {if (element < pivot) left.push(element);else if (element > pivot) right.push(element);else equal.push(element);}return [...quickSort(left), ...equal, ...quickSort(right)];
}

时间复杂度

  • 平均情况:O(n log n)

  • 最坏情况:O(n²)(当选择的基准总是最大或最小元素时)

优化技巧:三数取中法选择基准,对小数组使用插入排序

2. 归并排序(Merge Sort)

原理:分治法策略,将数组分成两半,分别排序,然后合并两个有序数组。

function mergeSort(arr) {if (arr.length <= 1) return arr;const mid = Math.floor(arr.length / 2);const left = arr.slice(0, mid);const right = arr.slice(mid);return merge(mergeSort(left), mergeSort(right));
}function merge(left, right) {let result = [];let leftIndex = 0;let rightIndex = 0;while (leftIndex < left.length && rightIndex < right.length) {if (left[leftIndex] < right[rightIndex]) {result.push(left[leftIndex]);leftIndex++;} else {result.push(right[rightIndex]);rightIndex++;}}return result.concat(left.slice(leftIndex)).concat(right.slice(rightIndex));
}

时间复杂度:始终为O(n log n)

特点:稳定排序,适合链表排序,需要额外空间

3. 堆排序(Heap Sort)

原理:利用堆这种数据结构所设计的一种排序算法,是一种选择排序。

function heapSort(arr) {let len = arr.length;// 构建最大堆for (let i = Math.floor(len / 2) - 1; i >= 0; i--) {heapify(arr, len, i);}// 一个个取出堆顶元素for (let i = len - 1; i > 0; i--) {[arr[0], arr[i]] = [arr[i], arr[0]]; // 将当前最大元素移到数组末尾heapify(arr, i, 0); // 重新调整堆}return arr;
}function heapify(arr, len, i) {let largest = i;let left = 2 * i + 1;let right = 2 * i + 2;if (left < len && arr[left] > arr[largest]) {largest = left;}if (right < len && arr[right] > arr[largest]) {largest = right;}if (largest !== i) {[arr[i], arr[largest]] = [arr[largest], arr[i]];heapify(arr, len, largest);}
}

时间复杂度:始终为O(n log n)

特点:原地排序,不稳定,适合大数据集

四、其他实用排序算法

1. 计数排序(Counting Sort)

原理:适用于一定范围内的整数排序,统计每个元素出现的次数,然后按顺序输出。

function countingSort(arr, maxValue) {const bucket = new Array(maxValue + 1).fill(0);let sortedIndex = 0;// 计数for (let i = 0; i < arr.length; i++) {bucket[arr[i]]++;}// 输出结果for (let j = 0; j < bucket.length; j++) {while (bucket[j] > 0) {arr[sortedIndex++] = j;bucket[j]--;}}return arr;
}

时间复杂度:O(n + k),k为数据范围

适用场景:整数排序且范围不大

2. 桶排序(Bucket Sort)

原理:将数组分到有限数量的桶里,每个桶再分别排序。

function bucketSort(arr, bucketSize = 5) {if (arr.length === 0) return arr;// 确定最小最大值let minValue = arr[0];let maxValue = arr[0];for (let i = 1; i < arr.length; i++) {if (arr[i] < minValue) {minValue = arr[i];} else if (arr[i] > maxValue) {maxValue = arr[i];}}// 初始化桶const bucketCount = Math.floor((maxValue - minValue) / bucketSize) + 1;const buckets = new Array(bucketCount);for (let i = 0; i < buckets.length; i++) {buckets[i] = [];}// 分配到桶中for (let i = 0; i < arr.length; i++) {buckets[Math.floor((arr[i] - minValue) / bucketSize)].push(arr[i]);}// 对每个桶排序并合并arr.length = 0;for (let i = 0; i < buckets.length; i++) {insertionSort(buckets[i]); // 使用插入排序或其他排序算法for (let j = 0; j < buckets[i].length; j++) {arr.push(buckets[i][j]);}}return arr;
}

时间复杂度:平均O(n + k),最坏O(n²)

适用场景:数据均匀分布在一定范围内

五、算法比较与选择建议

算法平均时间复杂度最坏时间复杂度空间复杂度稳定性适用场景
冒泡排序O(n²)O(n²)O(1)稳定小数据集或教学
选择排序O(n²)O(n²)O(1)不稳定交换成本高的场景
插入排序O(n²)O(n²)O(1)稳定小数据集或基本有序
快速排序O(n log n)O(n²)O(log n)不稳定通用排序,性能好
归并排序O(n log n)O(n log n)O(n)稳定大数据集,需要稳定排序
堆排序O(n log n)O(n log n)O(1)不稳定大数据集,原地排序
计数排序O(n + k)O(n + k)O(k)稳定整数且范围小
桶排序O(n + k)O(n²)O(n + k)稳定数据均匀分布

选择建议

  1. 小数据集(n < 100):插入排序简单高效

  2. 通用场景:快速排序通常是最佳选择

  3. 需要稳定排序:归并排序

  4. 内存有限:堆排序

  5. 已知数据范围:计数排序或桶排序

六、JavaScript引擎中的排序实现

现代JavaScript引擎(如V8)的Array.prototype.sort()实现通常采用混合排序策略:

  • 对于小数组(长度<10),使用插入排序

  • 对于大数组,使用快速排序或归并排序

  • 针对特定类型数组(如整数数组)可能有优化路径

七、性能测试示例

function testSortPerformance(sortFn, arr) {const start = performance.now();sortFn([...arr]); // 使用副本避免修改原数组const end = performance.now();return end - start;
}const largeArray = Array.from({length: 10000}, () => Math.floor(Math.random() * 10000));console.log('冒泡排序:', testSortPerformance(bubbleSort, largeArray), 'ms');
console.log('快速排序:', testSortPerformance(quickSort, largeArray), 'ms');
console.log('原生排序:', testSortPerformance(arr => arr.sort((a, b) => a - b), largeArray), 'ms');

八、总结

排序算法是计算机科学的基础,理解各种排序算法的原理和实现对于JavaScript开发者来说至关重要。虽然现代JavaScript引擎提供了高效的排序实现,但在某些特定场景下(如处理特定结构的数据或需要优化性能时),自定义排序算法可能更为合适。

选择排序算法时应考虑:

  1. 数据规模

  2. 数据初始有序程度

  3. 是否需要稳定排序

  4. 内存限制

  5. 数据特征(如整数、范围等)

通过理解各种排序算法的特性和适用场景,您可以针对具体问题选择最合适的解决方案,甚至组合多种算法以获得最佳性能。

相关文章:

JavaScript排序算法详解:从基础到高级

排序是编程中最基本也是最重要的操作之一。JavaScript作为一门广泛应用于Web开发的语言&#xff0c;提供了内置的排序方法&#xff0c;但了解各种排序算法的原理和实现对于开发者来说仍然至关重要。本文将深入探讨JavaScript中常见的排序算法&#xff0c;帮助您理解它们的原理、…...

chromedriver 下载失败

问题描述 chromedriver 2.46.0 下载失败 淘宝https://registry.npmmirror.com/chromedriver/2.46/chromedriver_win32.zip无法下载 解决方法 找到可下载源 https://cdn.npmmirror.com/binaries/chromedriver/2.46/chromedriver_win32.zip &#xff0c;先将其下载到本地目录(D…...

Weather app using Django - Python

我们的任务是使用 Django 创建一个 Weather 应用程序&#xff0c;让用户可以输入城市名称并查看当前天气详细信息&#xff0c;例如温度、湿度和压力。我们将通过设置一个 Django 项目&#xff0c;创建一个视图来从 OpenWeatherMap API 获取数据&#xff0c;并设计一个简单的模板…...

机器视觉2,硬件选型

机器视觉1&#xff0c;学习了硬件的基本知识和选型&#xff0c;现在另外的教材巩固知识 选相机 工业相机选型的保姆级教程_哔哩哔哩_bilibili 1.先看精度多少mm&#xff0c;被检测物体长宽多少mm》分辨率&#xff0c; 选出合理范围内的相机 2.靶面尺寸&#xff0c;得出分…...

自定义序列生成器之单体架构实现

主键 ID VS 业务 ID 在数据库设计中&#xff0c;除了主键 ID&#xff0c;一般还需要一个具有唯一索引的业务 ID。二者承担的职责不一样&#xff0c;它们共同满足了我们对于 技术实现 和 业务需求 的双重目标 1. 职责分离原则 主键 ID 业务唯一标识 ID 作用 保证数据库层面…...

电阻电容的选型

一、电阻选型 1.1安装方式 贴片电阻体积小&#xff0c;适用于SMT生产&#xff1b;功率小&#xff1b;易拆解插件电阻体积大&#xff1b;功率大&#xff1b;不易脱落 1.2阻值 电阻的阻值是离散的&#xff0c;其标称阻值根据精度分为E6、E12、E24、E48、E96、E192六大系列&am…...

12.springCloud AlibabaSentinel实现熔断与限流

目录 一、Sentinel简介 1.官网 2.Sentinel 是什么 3.Sentinel 的历史 4.Sentinel 基本概念 资源 规则 5.Sentinel 功能和设计理念 (1).流量控制 什么是流量控制 流量控制设计理念 (2).断降级 什么是熔断降级 熔断降级设计理念 (3).系统自适应保护 6.主要工作机制…...

Cookie 和 Session:Web 身份验证的核心机制

文章目录 一、Cookie&#xff1a;客户端存储的小数据块**核心特性****典型应用场景**二、Session&#xff1a;服务器端的会话存储**核心特性****典型应用场景**三、Cookie vs Session&#xff1a;核心区别对比四、最佳实践与扩展 一、Cookie&#xff1a;客户端存储的小数据块 …...

vSOME/IP与ETAS DSOME/IP通信的问题解决方案

✅ 一、服务版本不匹配导致 Handover 问题 —— 需要更新 VSOMEIP 代码逻辑 📌 问题描述: 在 SOME/IP 通信中,发布者(offer)与订阅者(subscribe)之间存在服务版本不一致的问题,导致 Handover(切换)失败。 ✅ 解决方案: 需要在 offer_service 和 subscribe 接口中…...

修改vscode切换上一个/下一个标签页快捷键

装了vim后一直没找到切tab页的快捷键 Code>Preferences>Keyboard Shortcuts on macOS 搜索这2个选项 我设置成了commandh 向前切换&#xff0c;commandl向后切换&#xff0c;贴合vim的方向设置 workbench.action.previousEditor commandh workbench.action.nextEdit…...

三大中文wordpress原创主题汉主题

汉主题 汉主题是一款极具特色的 WordPress 主题&#xff0c;由国内专业团队精心打造&#xff0c;专为中文用户设计。其设计灵感源自博大精深的汉文化&#xff0c;将传统文化元素与现代网页设计理念巧妙融合&#xff0c;呈现出独特而典雅的风格。无论是用于个人博客展示文学创作…...

软考-系统架构设计师-第十五章 信息系统架构设计理论与实践

信息系统架构设计理论与实践 15.2 信息系统架构风格和分类15.3 信息系统常用的架构模型15.4 企业信息系统总体框架15.5 信息系统架构设计方法 15.2 信息系统架构风格和分类 信息系统架构风格 数据流体系结构风格&#xff1a;批处理、管道-过滤器调用/返回体系结构风格&#x…...

Redis缓存-数据淘汰策略

数据淘汰策略就是&#xff0c;当redis内存满的时候&#xff0c;此时在向redis添加新的key&#xff0c;那么redis会按照某一种规则将内存中的数据删掉&#xff0c;这种删除数据的规则成为内存的淘汰策略。 redis支持8中淘汰策略 1.noeviction&#xff0c;这种是redis默认的情况…...

52. N 皇后 II【 力扣(LeetCode) 】

文章目录 零、原题链接一、题目描述二、测试用例三、解题思路四、参考代码 零、原题链接 52. N 皇后 II 一、题目描述 n 皇后问题 研究的是如何将 n 个皇后放置在 n n 的棋盘上&#xff0c;并且使皇后彼此之间不能相互攻击。【补充&#xff1a;不能互相攻击就是要求一个皇后的…...

MySQL 8 完整安装指南(Ubuntu 22.04)

MySQL 8 完整安装指南&#xff08;Ubuntu 22.04&#xff09; 本教程详细说明如何在 Ubuntu 22.04 上安装和配置 MySQL 8&#xff0c;包含安全优化及远程访问设置。 1️⃣ 添加 MySQL 官方 APT 仓库 官网仓库下载地址&#xff1a;MySQL APT 仓库下载页 下载仓库配置包&#…...

C++ 标准输入输出 -- <iostream>

<iostream>库是 C++ 标准库中用于输入输出操作的头文件。 <iostream> 定义了几个常用的流类和操作符,允许程序与标准输入输出设备(如键盘和屏幕)进行交互。 以下是<iostream>库的详细使用说明,包括其主要类和常见用法示例。 主要类 std::istream:用于…...

记一次sql按经纬度计算距离

具体代码&#xff1a; ROUND函数在mysql可以用来计算经纬度&#xff0c;代码如下&#xff1a; SELECTa.store_name_sfa as storeName,a.storeid_sfa as store_id,a.link_man_sfa as link_man,a.link_phon_sfa as link_phone,a.photo as image_url,a.district,a.street,ROUND(6…...

安卓jetpack compose学习笔记-UI基础学习

哲学知识应该用哲学的方式学习&#xff0c;技术知识也应该用技术的方式学习。没必要用哲学的态度来学习技术。 学完安卓技术能做事就ok了&#xff0c;安卓技术肯定是有哲学的&#xff0c;但是在初学阶段没必要讨论什么安卓哲学。 学习一们复杂技术的路径有很多&#xff0c;这里…...

线性回归用于分类

线性回归本身是一种用于回归问题的技术&#xff0c;即预测一个连续的目标变量值。然而&#xff0c;线性回归也可以被改造或结合其他技术来用于分类问题&#xff0c;尽管这不是其最直接或最常见的用途。以下是几种将线性回归应用于分类问题的方法或相关概念&#xff1a; 阈值划分…...

解锁电商新势能:商城系统自动 SaaS 多开功能深度解析

在电商行业加速向精细化、多元化运营转型的当下&#xff0c;传统的商城系统部署模式已难以满足企业快速拓展业务的需求。此时&#xff0c;商城系统自动 SaaS 多开功能横空出世&#xff0c;以智能、高效、灵活的特性&#xff0c;成为众多电商企业突破发展瓶颈的关键利器。这一功…...

蓝桥杯_DS18B20温度传感器---新手入门级别超级详细解析

目录 一、引言 DS18B20的原理图 单总线简介&#xff1a; ​编辑暂存器简介&#xff1a; DS18B20的温度转换与读取流程 二、代码配置 maic文件 疑问 关于不同格式化输出符号的使用 为什么要rd_temperature()/16.0&#xff1f; onewire.h文件 这个配置为什么要先读lo…...

C++中锁与原子操作的区别及取舍策略

文章目录 锁与原子操作的基本概念锁&#xff08;Lock&#xff09;原子操作&#xff08;Atomic Operations&#xff09; 锁与原子操作的区别1. **功能**2. **性能**3. **复杂性**4. **适用场景** 锁与原子操作的取舍策略1. **简单变量操作**2. **复杂共享资源**3. **性能敏感场景…...

ESP32对接巴法云实现配网

目录 序言准备工作巴法云注册与使用Arduino准备 开发开始配网 序言 本文部分内容摘抄原创作者巴法云-做优秀的物联网平台 代码有部分修改并测试运行正常 巴法云支持免费用户通过开发对接实现各智能音箱设备语音控制智能家居设备&#xff0c;并有自己的App进行配网和控制&…...

《深度剖析:基于Meta的GameFormer构建自博弈AI游戏代理》

自博弈AI游戏代理&#xff0c;是一种具备自主学习和自我提升能力的人工智能系统。它打破了传统AI依赖预设规则和固定策略的局限&#xff0c;能够在游戏过程中不断与自身进行对战&#xff0c;通过反复博弈来积累经验、优化策略&#xff0c;从而实现智能水平的持续提升 。这种独特…...

C++语法系列之类型转换

前言 类型转换是经常存在的情况&#xff0c;类型转换分为隐式类型转化 和 显式类型转化 隐式类型转化&#xff1a;编译器在编译阶段自动进行&#xff0c;能转就转&#xff0c;不能转就编译失败 double i 3.3; int b i; //隐式类型转化 double -> intC搞出来了四种强制类…...

Qwen3 技术报告解读一

&#x1f4d8; Qwen3 技术报告解读&#xff1a;通义千问系列新成员的技术亮点与能力分析 一、论文写了什么&#xff1f; 本文来自阿里通义实验室发布的 《Qwen3 Technical Report》&#xff0c;介绍了其最新一代大语言模型 Qwen3 的技术架构、训练方法以及在多个关键任务上的…...

详解开漏输出和推挽输出

开漏输出和推挽输出 以上是 GPIO 配置为输出时的内部示意图&#xff0c;我们要关注的其实就是这两个 MOS 管的开关状态&#xff0c;可以组合出四种状态&#xff1a; 两个 MOS 管都关闭时&#xff0c;输出处于一个浮空状态&#xff0c;此时他对其他点的电阻是无穷大的&#xff…...

【八股消消乐】索引失效与优化方法总结

&#x1f60a;你好&#xff0c;我是小航&#xff0c;一个正在变秃、变强的文艺倾年。 &#x1f514;本专栏《八股消消乐》旨在记录个人所背的八股文&#xff0c;包括Java/Go开发、Vue开发、系统架构、大模型开发、具身智能、机器学习、深度学习、力扣算法等相关知识点&#xff…...

一步一步配置 Ubuntu Server 的 NodeJS 服务器详细实录——4. 配置服务器终端环境 zsh , oh my zsh, vim

前言 通过前面几篇文章&#xff0c;我们顺利的 安装了 ubuntu server 服务器&#xff0c;并且配置好了 ssh 免密登录服务器&#xff0c;也安装好了 服务器常用软件安装,接下来&#xff0c;我们要仔细的配置一下我们的终端环境&#xff0c;让服务器的终端更加好用。 一般情况下…...

数据安全合规体系构建的“三道防线“

引言 "三道防线"模型架构图 #mermaid-svg-wbeppAbwa3Vb3nL2 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-wbeppAbwa3Vb3nL2 .error-icon{fill:#552222;}#mermaid-svg-wbeppAbwa3Vb3nL2 .error-text{fi…...