JavaScript算法-排序算法

article/2025/11/9 19:40:12

​ 此生之路,我将走过;走过这一次,便再也无法重来。所有力所能及的善行,所有充盈于心的善意,我将毫不吝惜,即刻倾予。我将再不拖延,再不淡漠,只因此生之路,再也无法重来。

对计算机中存储的数据执行的两种最常见操作是排序和索引。下述阐述的排序方式,暂且都是用数组进行测试(从小到大)。

var dataAry = [5, 4, 3, 7, 1, 2, 8, 6, 9]; // 测试数组
/***【工具方法】交换数组中两个值* @param ary 数组* @param i 下标i* @param j 下标j*/
function swap(ary, i, j) {var temp = ary[i];ary[i] = ary[j];ary[j] = temp;
}

冒泡排序

之所以称为冒泡排序是因为使用这种排序算法时,数据值会像气泡一样从数组的一端漂浮到另一端。如,从小到大排序:其会比较相邻的数据,当左侧值大于右侧值时将它们进行交换。

冒泡排序算法的运作如下:(从小到大)

  1. 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
  2. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
  3. 针对所有的元素重复以上的步骤,除了最后一个。
  4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
function bubbleSort(dataAry) {var len = dataAry.length;for(var i = 0; i < len - 1; i++) {for(var j = 0; j < len - i - 1; j++) {if(dataAry[j] > dataAry[j+1]) {swap(dataAry, j, j+1);}}}return dataAry;
}
// 测试
console.log(bubbleSort(dataAry)); // [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
543712869原始数组
435127689第1轮
341256789第2轮
312456789第3轮
123456789第4轮
123456789第5轮
123456789第6轮
123456789第7轮
123456789第8轮
123456789第9轮

通过上述可知,第4轮过后,排序就已完成(数组已经有序了),上述算法仍然兢兢业业的执行了9轮。所以,对于冒泡排序,我们可以做优化。

优化方式

第一种方式: 在内循环中添加标记,如果有交换,则数组无序;无交换,则数组已有序。

function bubbleSort(dataAry) {var len = dataAry.length;for(var i = 0; i < len - 1; i++) {var isSorted = true;for(var j = 0; j < len - i - 1; j++) {if(dataAry[j] > dataAry[j+1]) {swap(dataAry, j, j+1);isSorted = false;}}if (isSorted) break;}return dataAry;}

上述优化结束后,4轮执行便可结束。但仔细查看,会发现 swap 仍然存在多余的比较(第二轮结束后,5、6、7、8、9均已就位,但是第三轮仍需要比较其中部分 – 8、9不会再比较)。

第二种方式: 对有序去界定,记录下最后一次元素交换的位置。

function bubbleSort(dataAry) {var len = dataAry.length;// 记录最后一次交换的位置var lastExchangeIndex = 0// 无序边界(每次比较截止位置)var sortBorder = len - 1for(var i = 0; i < len - 1; i++) {var isSorted = true;for(var j = 0; j < sortBorder; j++) {if(dataAry[j] > dataAry[j+1]) {swap(dataAry, j, j+1);isSorted = false;// 更新最后一次交换元素的位置lastExchangeIndex = j}}sortBorder = lastExchangeIndexif (isSorted) break;}return dataAry;
}

第三种方式: 鸡尾酒排序(双向交换),大家自行查阅!

选择排序

​ 从数组的第一个数据开始,将第一个数据和其他数据进行比较。它的工作原理是每一次从待排序的数据中选出最小(或最大)的一个数据,存放在序列的起始位置,直到全部待排序的数据元素排完。

function selectionSort(dataAry) {var len = dataAry.length;for(var i = 0; i < len - 1; i++) {for(var j = i + 1; j < len; j++) {if(dataAry[i] > dataAry[j]) {swap(dataAry, i , j);}}}return dataAry;
}
// 测试
console.log(selectionSort(dataAry)); // [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]

插入排序

​ 插入排序有两个循环,外循环将数组元素挨个移动,而内循环则对外循环中选定的元素及它后面的那个元素比较。如果外循环中选中元素小,那么数组元素会向右移动,为内循环中的这个元素腾出位置。每步将一个待排序的纪录,按其关键码值的大小插入前面已经排序的文件中适当位置上,直到全部插入完为止。

直接插入排序算法的说明<有1-9张标有是数字的卡片>:

  1. 取出第一张卡片直接放到桌子最左侧
  2. 取出第二张卡片,如果该卡片标注的数字小于第一张,则将第一张卡片向右移动一格;否则放到右侧;
  3. 取出第n张卡片,将该卡片与第n-1、n-2···第1张对比,小于等于则右移继续对比,大于则停止对比将该值插入对应位置。
function insertionSort(dataAry) {var temp, inner;// 外循环(跳过第一个值,因为第一个直接放到最左侧)for(var outer = 1, len = dataAry.length; outer < len; outer++) {temp = dataAry[outer];inner = outer;// 内循环,比左侧值小就移动让位while(inner > 0 && (dataAry[inner - 1] >= temp)) {dataAry[inner] = dataAry[inner - 1];inner--;}dataAry[inner] = temp;}return dataAry
}
// 测试
console.log(insertionSort(dataAry)); // [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]

希尔排序

​ 该算法在插入排序的基础上做了相应改善。其需要预先(或动态)定义一个间隔序列来表示在排序过程中进行比较的元素之间的间隔。对被间隔的每组元素使用直接插入排序算法排序;随着间隔序列中的数逐渐减小,每组包含的元素越来越多,当间隔减至1时,整个文件恰被分成一组,算法便终止。这确保了在开始最后一次处理时,大部分元素都已在正确位置,必须再进行多次数据交换,这就是希尔排序比插入排序更高效的地方。

希尔排序算法说明:

  1. 先取一个小于n的整数d1作为第一个间隔,把文件的全部记录分组。所有距离为d1的倍数的记录放在同一个组中。先在各组内进行直接插入排序;
  2. 取第二个间隔值d2<d1重复上述的分组和排序;
  3. 直至所取的间隔为1,即所有记录放在同一组中进行直接插入排序为止。
    希尔排序
/*** 希尔排序* @param dataAry 数据数组* @param gaps    间隔数组*/
function shellSort(dataAry, gaps) {var temp, inner, currentGap;// 遍历间隔for(var g = 0, gLen = gaps.length; g < gLen; g++) {// 当前间隔currentGap = gaps[g];// 直接插入法外循环for(var outer = currentGap, len = dataAry.length; outer < len; outer++) {temp = dataAry[outer];inner = outer;// 直接插入法内循环(注意对比间隔值)while(inner >= currentGap && (dataAry[inner - currentGap] >= temp)) {dataAry[inner] = dataAry[inner - currentGap];inner = inner - currentGap;}dataAry[inner] = temp;}}return dataAry;
}
// 测试
console.log(shellSort(dataAry, [3, 2, 1])); // [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]

归并排序

把一系列排好序的子序列合并成一个大的完整有序序列。归并排序通常使用递归来实现。

自顶向下的归并排序(递归)

自顶向下的归并排序(递归)

function mergeSort(dataAry) {if(dataAry.length === 1) {return dataAry;}var mid = Math.floor(dataAry.length/2);var leftAry = dataAry.slice(0, mid);var rightAry = dataAry.slice(mid);return merge(mergeSort(leftAry), mergeSort(rightAry));
}
/*** 合并数组* @param dataAry*/
function merge(leftAry, rightAry) {var result = [];while(leftAry.length > 0 && rightAry.length > 0) {if(leftAry[0] < rightAry[0]) {result.push(leftAry.shift());}else {result.push(rightAry.shift());}}return result.concat(leftAry).concat(rightAry);
}
// 测试
console.log(mergeSort(dataAry)); // [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]

​ 上述mergeSort()被执行了2n-1次(上述被执行了17次),一旦数组元素个数增多,采用递归的方式将不再可取,元素个数超过1500在Firfox下就会造成栈溢出!

自底向上的归并排序(迭代)

​ 首先将数据集分解为一组只有一个元素的数组,然后通过创建一组左右数组将其合并,每次合并确保数据已排好序,直到最后所有数据排序完成。

这里写图片描述

function mergeSort(dataAry) {if(dataAry.length < 2) {return dataAry;}var step = 1; // 控制子序列大小var left, right; // 左、右下标while(step < dataAry.length) {left = 0;right = step;while((right + step) <= dataAry.length) {mergeArrays(dataAry, left, left+step, right, right+step);left = right + step;right = left + step;}// 不能被分组的元素if(right < dataAry.length) {mergeArrays(dataAry, left, left+step, right, dataAry.length);}step = step * 2;}return dataAry;
}/*** 合并数组<包含头,不包含尾>* @param ary* @param startLeft * @param endLeft* @param startRight* @param endRight*/
function mergeArrays(ary, startLeft, endLeft, startRight, endRight) {var leftAry = new Array(endLeft - startLeft + 1),rightAry = new Array(endRight - startRight + 1);var k = startLeft;for(var i = 0; i < (leftAry.length - 1); i++) {leftAry[i] = ary[k];k++;}leftAry[leftAry.length - 1] = Infinity; // 哨兵值k = startRight;for(var j = 0; j < (rightAry.length - 1); j++) {rightAry[j] = ary[k];k++;}rightAry[rightAry.length - 1] = Infinity; // 哨兵值// 对比左右元素大小var m = 0, n = 0;for(var k = startLeft; k < endRight; k++) {if(leftAry[m] <= rightAry[n]) {ary[k] = leftAry[m];m++;}else {ary[k] = rightAry[n];n++;}}
}
// 测试
console.log(quickSort(dataAry)); // [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]

快速排序

​ 快速排序是处理大数据集最快的排序算法之一。其核心思想为“分而治之”。设立基值,通过递归的方式将数据一次分解为包含较小元素和较大元素的不同子序列,然后不断重复,直到所有数据变为有序。

快速排序算法说明:

  1. 选择一个基准元素,将列表分隔为两个子序列;
  2. 将所有小于基准元素的数据放在基准值的左侧,大于基准值元素的数据放在基准值右侧;
  3. 分别对较小元素的子序列和较大元素的子序列重复步骤1和2。

快速排序<方式一>:单边。从左侧起开始循环数组与基值进行对比

function quickSort(dataAry) {if(dataAry.length === 0) {return [];}var left = [], right = [];var pivot = dataAry[0];for(var i = 1; i < dataAry.length; i++) {dataAry[i] < pivot ? left.push(dataAry[i]) : right.push(dataAry[i]);}return quickSort(left).concat(pivot, quickSort(right));
}
// 测试
console.log(quickSort(dataAry)); // [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]

快速排序<方式二>:双边。从左、右两侧一起移动,与基值进行对比

这里写图片描述

function quickSort(arr, left, right) {if (left > right) return;var temp,i = left,j = right;// 设基准值var pivot = arr[left];while (i != j) {// 先从右侧开始(找到小于基值的第一个数据)while (i < j && arr[j] >= pivot) {j--;}// 然后从左侧开始(找到大于基值的第一个数据)while (i < j && arr[i] <= pivot) {i++;}// 交换数据上述两个元素,继续查找,直到i和j相遇if (i < j) {temp = arr[i];arr[i] = arr[j];arr[j] = temp;}}// 将基值与相遇数据进行交换<确保了基值位于元素中央位置>temp = arr[i];arr[i] = arr[left];arr[left] = temp;// 基值左侧数组按上述方式递归执行quickSort(arr, left, i - 1);// 基值右侧数组按上述方式递归执行quickSort(arr, i + 1, right);return arr;
}
// 测试
console.log(quickSort(dataAry, 0, dataAry.length - 1)); // [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]

计数排序

计数排序利用数组下标来确定元素的位置。适用于一定范围内的整数排序。
以上述提供的测试数组为例 var dataAry = [5, 4, 3, 7, 1, 2, 8, 6, 9]; // 测试数组 其满足计数排序的场景。

计数排序说明:

  1. 确定数组的取值范围为 0 ~ 10
  2. 创建一个长度为11的统计数组countArray,下标从0开始,到10;设置初始值均为0
  3. 循环排序数组按照值对号入座,同时对应数组下标的元素进行加1操作
  4. 遍历countArray数组,输出数组的下标值,元素是几就输出几次
function countSort(dataAry) {// 获取最大数,并创建数组let maxNumber = Math.max.call(undefined, ...dataAry)let countArray = new Array(maxNumber+1).fill(0)// 填充统计数组for (let data of dataAry) {countArray[data]++}// 遍历统计结果数组let sortedArray = new Array(dataAry.length)let index = 0for (let i = 0, len = countArray.length;i < len; i++) {for (let j = 0; j < countArray[i]; j++) {sortedArray[index++] = i}}return sortedArray
}
/ 测试
console.log(countSort(dataAry)); // [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]

优化

[ 91 , 98 , 97 , 92 , 93 , 96 , 95 ] [91,98,97,92,93,96,95] [91,98,97,92,93,96,95] 统计数组的长度,以数列 最 大 值 − 最 小 值 + 1 最大值-最小值+1 +1 作为统计数组的长度

不适合场景

  1. 数列最大和最小值差距过大
  2. 数列元素不是整数时

这些场景,可以考虑桶排序,这里不再赘述,自行查阅!

总结

冒泡排序、选择排序、插入排序为基本排序算法,希尔排序、归并排序(迭代)、快速排序为高级排序算法

排序算法时间复杂度是否稳定排序100条所耗时间10000条所耗时间100000条所耗时间
冒泡排序 O ( n 2 ) O(n^2) O(n2)稳定16毫秒584毫秒54619毫秒
选择排序 O ( n 2 ) O(n^2) O(n2)稳定<1毫秒183毫秒18175毫秒
插入排序 O ( n 2 ) O(n^2) O(n2)稳定<1毫秒27毫秒2660毫秒
希尔排序介于 O ( n 2 ) O(n^2) O(n2) O ( n l o g n ) O(nlogn) O(nlogn)稳定<1毫秒13毫秒1384毫秒
归并排序(迭代) O ( n l o g n ) O(nlogn) O(nlogn)稳定<1毫秒6毫秒40毫秒
快速排序(方式一) O ( n l o g n ) O(nlogn) O(nlogn)不稳定<1毫秒17毫秒98毫秒
快速排序(方式二) O ( n l o g n ) O(nlogn) O(nlogn)不稳定<1毫秒3毫秒13毫秒
计数排序作用范围有限稳定<1毫秒1毫秒30毫秒

稳定排序和不稳定排序: 相同的元素在排序后仍然保持着排序前的顺序,则为稳定排序(第二个6仍然处于第一个6后面)。

示例58668
不稳定排序35668
稳定排序35668

http://chatgpt.dhexx.cn/article/CWg8wkgA.shtml

相关文章

一个简单的js快速排序算法

简介&#xff1a; 快速排序是对冒泡排序的一种改进。它的基本思想是&#xff1a; 通过一趟排序将要排序的数据分割成独立的两部分&#xff0c;其中一部分的所有数据都比另外一部分的所有数据都要小&#xff0c;然后再按此方法对这两部分数据分别进行快速排序&#xff0c;整个排…

js排序算法详解-插入排序

全栈工程师开发手册 &#xff08;作者&#xff1a;栾鹏&#xff09; js系列教程5-数据结构和算法全解 js排序算法详解-插入排序 插入排序的原理其实很好理解&#xff0c;可以类比选择排序。选择排序时在两个空间进行&#xff0c;等于说每次从旧的空间选出最值放到新的空间&am…

JS 常见排序算法汇总(全)

文章目录 排序算法总结JS 十大排序算法冒泡排序单向冒泡双向冒泡选择排序插入排序快速排序归并排序桶排序基数排序计数排序 排序算法总结 JS 十大排序算法 冒泡排序 作为最简单的排序算法之一&#xff0c;冒泡排序感觉就像Abandon在单词书里出现的感觉一样&#xff0c;每次都…

常用排序算法(js)

复习排序算法 时间复杂度、空间复杂度、稳定新 排序算法时间复杂度空间复杂度稳定性冒泡O(n^2)O(1)是选择O(n^2)O(1)不是插入O(n^2)O(1)是希尔O(nlogn)O(1)不是快速O(nlogn)O(nlogn)不是 1. 冒泡排序 思路&#xff1a;每一轮都对相邻的两个元素进行比较&#xff0c;如果逆序…

JS实现快速排序算法

快速排序的基本思想是选择数组中的一个元素作为关键字&#xff0c;通过一趟排序&#xff0c;把待排序的数组分成两个部分&#xff0c;其中左边的部分比所有关键字小&#xff0c;右边的部分比所有关键字大。然后再分别对左右两边的数据作此重复操作&#xff0c;直到所有元素都有…

JavaScript排序算法专栏(JS十大排序算法详解)

一、冒泡排序 1、Explanation And Steps&#xff08;解释的步骤&#xff09; 冒泡排序&#xff08;Bubble Sort&#xff09;也是一种简单直观的排序算法。它重复地走访过要排序的数列&#xff0c;一次比较两个元素&#xff0c;如果他们的顺序错误就把他们交换过来。走访数列的…

JS实现经典的排序算法

几大经典排序算法实现&#xff1a; 1.排序算法介绍&#xff1a; 2.基本算法 2.1 冒泡排序 function bubbleSort(arr) {var len arr.lengthfor (var i 0; i < len; i) {for (var j 0; j < len - 1 - i; j) {if (arr[j] > arr[j 1]) {;[arr[j], arr[j 1]] [arr…

js实现常见排序算法

文章目录 前言一、排序相关概念1.比较排序和非比较排序比较排序非比较排序 2.稳定性和不稳定性 二、各排序算法对比三、排序算法中的通用函数以及对数器1.通用函数交换函数取两索引的中间下标&#xff0c;中间值mid 2.对数器 四、各排序算法的实现1.冒泡排序算法思想过程图解代…

js-常见排序算法

目录 一、选择排序 二、冒泡排序 三、插入排序 1. 希尔排序 四、归并排序 五、快速排序 版权声明&#xff1a;本文为博主原创文章&#xff0c;若文章中有错误请联系博主改正&#xff0c;请不要恶意留言(不喜欢请绕道)。欢迎大家转载&#xff0c;转载时请注明原文地址:https://b…

3.JS排序算法之选择排序

选择排序&#xff08;selectSort&#xff09;&#xff0c;顾名思义&#xff0c;每次选择最值进行排序 目录 一、选择排序算法原理 二、选择排序算法分析 三、选择排序算法应用实例 四、选择排序的适用场景 一、选择排序算法原理 1.思路 选择排序的实现思路是从未排序序列中…

JavaScript实现十大排序算法(图文详解)

冒泡排序 排序的效果图 解法 当前解法为升序 冒泡排序的特点&#xff0c;是一个个数进行处理。第i个数&#xff0c;需要与后续的len-i-1个数进行逐个比较。 为什么是 len-i-1个数&#xff1f; 因为数组末尾的i个数&#xff0c;已经是排好序的&#xff0c;确认位置不变的了。…

JS 常见的排序算法

工作中算法不常用&#xff0c;但是排序经常用。因此在这里整理了几种JS中常见的排序算法。 冒泡排序 1、算法思想&#xff1a;判断两个相邻元素&#xff0c;大于则交换位置 2、算法步骤 从数组中第一个数开始&#xff0c;依次与下一个数比较并次交换比自己小的数&#xff0c…

详细解析十大排序算法(js实现)

详细解析十大排序算法js实现 算法概述1.冒泡排序1.1算法描述1.2动图演示1.3代码实现 2.选择排序2.1算法描述2.2动图演示2.3代码实现2.4算法分析 3.插入排序3.1算法描述3.2动图演示3.3代码实现3.4算法分析 4.希尔排序4.1算法描述4.2动图演示4.3代码实现4.4算法分析 5.归并排序5.…

Excel中使用SUMIFS公式时计算结果不正确(原因:有超过15位数字的长编码)

在使用SUMIFS公式进行多条件求和时发现求和结果部分正确&#xff0c;部分错误。 常见原因&#xff1a; 1、公式错误 2、数据格式有问题或者有空格等 仔细检查后发现并不是上述两个原因&#xff0c;而是求和的公式中涉及到超过15位数字的长编码。常见的SUMIF、SUMIFS、COUNTIF等…

mybatis-plus使用

1 mybaitsplus简介 &#xff08;1&#xff09;对于mybatis功能的增强&#xff0c;具体的体现就是对于单表的操作都生成好了&#xff0c;分页&#xff0c;条件查询使用很方便&#xff0c;例如查询操作&#xff0c;直接使用wrapper的实现类&#xff0c;调用方法进行条件的增加&a…

四、Hibernate框架的API (三)-- Session对象

一、Session对象 1.Hibernate最重要的对象,只用使用hibernate与数据库操作&#xff0c;都用到这个对象2.该对象维护了一个Connection对象。代表了与数据库连接的会话。3.该对象实质上是对Connection对象的包装&#xff0c;在Connection对象基础之上&#xff0c;封装了一些方法。…

Criteria的用法

一、Hibernate提供了5种检索对象的方式 1.导航对象图检索方式&#xff1a;根据已经加载的对象导航到其他对象    from Emp e group by e.dept.deptName 2.OID检索方式&#xff1a;按照对象的OID来检索对象   get/load 3.HQL检索方式&#xff1a;使用面向对象的HQL查询语…

第13章:处理Excel电子表格(笔记)

13.1&#xff1a;Excel文档 13.2&#xff1a;安装openpyxl模块 pip install --user --Uopenpuxl2.6.2 这是安装2.6.2版本的&#xff0c;比较新的版本与学习的书籍的信息有一点不兼容 13.3&#xff1a;读取Excel文档 13.3.1&#xff1a;用openpyxl模块打开Excel文档 import …

关于 range.autofilter 和 VBA的 filter

1 range().autofilter容易出错的地方 1.1 range().autofilter的返回值 range().autofilter 返回值总是 true返回值并不是对象&#xff0c;而是执行一个筛选的操作所以 Sub test1_filter5() Dim rng1 As Range Set rng1 Range("b1:b20").AutoFilter(field:1, Crit…

Opencv学习之角点检测

Opencv学习之角点检测 角点检测 在图像处理和计算机视觉领域&#xff0c;兴趣点&#xff08;interest points&#xff09;&#xff0c;也被称作关键点&#xff08;key points&#xff09;、特征点&#xff08;feture points&#xff09;。它被大量用于解决物体识别、图像识别…