JavaScript 基础排序的实现(二)
继上一篇O(n^2)的排序算法后,这一篇主要记录O(n*logn)的排序算法
1.快排(快速排序)
这一算法的核心思想为,先随机选一个数作为标兵或者说是标记(这个数一般来说选择该无序数组的中间那个元素;此处笔者选取第一个实现算法,当选取完毕后以此标兵为参照将比这个数大的放到他的右边,比他小的放到左边.这样一趟排序过后就能让这个标记左边的数比他小,右边的数都比他大.下一趟排序则分别选取他的左半边数组和右半边数组重复之前的操作(此操作一般由递归实现)当传入的区间只有一个元素的时候那么开始返回不再继续调用函数自身(即为递归的终止条件)因为只有一个数字是必定有序的
递归实现代码如下:
//快速排序(递归)
function QuickSort(arr,start = 0,end = arr.length-1){
if(start<end){//递归结束条件
let [key,l,r] = [parseInt((start+end)/2),start,end];//key为标记的值,l和r 分别表示左边的下标和右边的下标
while(l<r){//当左右下标相遇即完成一趟排序,停止循环
while(l<r && arr[r] >= key){//从后往前找,如果对应下标的数比标记大则不做操作,下标继续前移
r--;
}//循环完成此时arr[r]
//而arr[l]的的值已经被key保存所以下一步则应该找出一个比key大却在key的左边的数放入arr[r]的位置
while(l<r && arr[l] <= key){
l++;
}
[arr[r],arr[l]] = [arr[l],arr[r]];
//这样一次循环就可以让一个在key左边比key大的数放到key的右边,比key小的放到左边
}
QuickSort(arr,start,l-1);//递归调用 -1 是为了避免区间重复
QuickSort(arr,l+1,end);
}
}
下图为将十万倒序数组变为正序的耗费时间:

下面列出非递归版本的快排:
function QuickSort2(arr,start = 0,end = arr.length-1){
let mid = [start,end];//建立一个数组用于存放需要快排的区间的参数,这样循环调用即可
if(start<end){
while(mid.length){//当参数数组中没有参数则停止循环
let [l,r] = [mid.shift(),mid.shift()];//取出数组中最前面的两个数,并对此区间上的数进行快排
let [low,height] = [l,r];
let key = arr[parseInt((l+r)/2)];//选取标兵
while(l<r){
while(l<r && arr[r] >= key){
r--;
}
arr[l] = arr[r];
while(l<r && arr[l] <= key){
l++;
}
arr[r] = arr[l];
arr[l] = key;
}
//当两个参数不相等,即该区间不止一个数时,则将下一个区间的参数存入参数数组
if(low<l)
mid.push(low,l);
if(l+1<height)
mid.push(l+1,height);
}
}
}
对十万逆序数组排序耗时如下:

由于非递归版本数组方法调用较多,故此花费时间略久于递归版本
2.希尔排序
希尔排序和快排一样都是不稳定排序,快速排序的不稳定是因为,标兵的选取并不能直接选取到刚好是区间中中间大小的数,所以当每个区间的第一个数刚好都是该区间最小的数,那么达到快排的最坏情况(以区间的第一个数为标兵为例). 而希尔排序其核心是插入排序所以当数组刚好为逆序时其效率最低,此时也就是希尔排序的最坏情况但与快排不同的是最坏情况的希尔排序速率并不会下降明显.
希尔排序算法的和心在于,利用了当待排序数组基本有序的时候插入排序效率极高的原理,即选取不同的步长对其进行插排,当步长为一时则与普通的插排没有区别,而效率的提升在于经过前期数次的插排使数组基本有序
其代码如下:
function ShellSort(arr) {
let steep = parseInt(arr.length / 2);//计算步长
let length = arr.length;
let tarr = [];//用于存放按步长分出的数组的临时数组
while (steep) {
//只有步长大于1才分组 否则直接插排
if ( steep > 1) {
for (let i = 0; i < steep; i++) {
//此循环将数组分组
for (let j = i; j < length; j = j + steep) {
tarr.push(arr[j]);
}
InsertionSort(tarr);//将分好组的数组进行插排
for (let j = i; j < length; j = j + steep) {
arr[j] = tarr.shift();
}
//排好序的数组映射回原数组
tarr.length = 0;//清空临时数组
}
}else
InsertionSort(arr);
steep = parseInt(steep / 2);
}
}
普通插排耗时如下:

希尔排序耗时如下:

3.归并排序
与快排和希尔排序不同的是归并排序是稳定排序
其核心思想在于当数组只有一个数时肯定是有序的,所以现将数组拆分为一个一个的数,然后在合并的过程中对齐排序,其弊端在于需要占用一个与原数组等长的临时数组空间来存放变量
递归版代码如下:
//归并排序(递归)
function Merge(arr,start,mid,end,temp) {//此函数用于合并数组
let [l,r] = [start,mid+1];//l标示左边需合并数组的初始下标,r同理标示右边数组的初始下标
while(l<=mid && r <= end){
if(arr[l]<=arr[r])
temp.push(arr[l++]);
else
temp.push(arr[r++]);
}
while(l<=mid){
temp.push(arr[l++]);
}
while(r<=end){
temp.push(arr[r++]);
}
while(start<=end){//将排好的临时数组中的数据复制回原数组
arr[start++] = temp.shift();
}
}
function RecursiveSort(arr,start = 0,end = arr.length-1,temp = []){
if(start < end){
let mid = parseInt((start+end)/2);//将数组分为两半
RecursiveSort(arr,start,mid,temp);
RecursiveSort(arr,mid+1,end,temp);
Merge(arr,start,mid,end,temp);//递归的回调途中进行数组的合并
}
}
递归版耗时:

非递归代码:
function RecursiveSort2(arr,start = 0,end = arr.length-1){
let mid = [start,end];
let pra = [];
while(mid.length){//获取参数列表
let [l,r] = [mid.shift(),mid.shift()];
pra.push(l,r);
let m = parseInt((l+r)/2);
if(m>l)
mid.push(l,m);
if(r>m+1)
mid.push(m+1,r);
}
while(pra.length){
let [r,l] = [pra.pop(),pra.pop()];
let m = parseInt((l+r)/2);
Merge(arr,l,m,r,[]); //此函数见上文
}
}
非递归耗时如下:

总结:
快排在数据量大时的优势较为明显,希尔排序适用于中量数据,而当空间要求不限时归并也不失为一种选择
JavaScript 基础排序的实现(二)的更多相关文章
- JavaScript基础知识总结(二)
JavaScript语法 二.数据类型 程序把这些量.值分为几大类,每一类分别叫什么名称,有什么特点,就叫数据类型. 1.字符串(string) 字符串由零个或多个字符构成,字符包括字母,数字,标点符 ...
- javascript基础学习(十二)
javascript之BOM 学习要点: 屏幕对象 History对象 Location对象 一.屏幕对象 Screen对象是一个由javascript自动创建的对象,该对象的主要作用是描述客户端的显 ...
- JavaScript 基础排序的实现(一)
作为一个有追求的前端,忙里偷闲(闲得发慌)地复习了一下基础的排序算法,以此文留念. 本篇主要记录O(n²)复杂度的基础算法O(nlogn)的算法将在下次有空(闲得发慌)时更新 在记录时发现Es6语法中 ...
- JavaScript基础入门教程(二)
说明 前一篇博客介绍了js以及一些关于js基本类型的简单知识,本篇博客将详细介绍js的基础类型,捎带介绍对象类型,更详细的对象类型的说明将后续再讲. js中类型的说明 js中的类型分为基本类型和对象类 ...
- Javascript基础系列之(二)变量
javascript 中变量通过var关键字(variable)来声明的. var school = "beijingyizhong" 也可以通过var 关键字给变量多个值. va ...
- JavaScript基础笔记(十二)Ajax
Ajax 一.XMLHttpRequest对象 一)XHR用法 var xhr = new XMLHttpRequest(); //open()方法,参数一:发送方法,参数二:请求的URL,参数三:是 ...
- javaScript 基础知识汇总(二)
1.运算符 术语或者叫法:一元运算符.二元运算符.运算元(参数) let x=0; x=5+2; //5和2为运算元,“+” 为二元运算符: x=-x; //"-" 为一元运算符 ...
- javascript基础总结之实例(二)
div的显示和隐藏 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://w ...
- 原生javascript 基础动画函数封装(二)
<!DOCTYPE html> <html> <head> <title></title> <style type="tex ...
随机推荐
- Jenkins+Gradle+Docker打docker镜像包上传至s3
gradle打包跟maven打包的环境搭建有相似之处,可参考maven打包https://www.cnblogs.com/chenchen-tester/p/6408815.html 进入Jenkin ...
- Win10系统,开机后提示Desktp不可用的故障解决方法。
WIN10桌面位置不可用的故障解决方法 今天电脑开机 (Win10系统).桌面空了,然后就蒙了. 系统并弹出以下框,如下图: 原因可能是因为昨天晚上熬夜测试脚本.网上下载的脚本大多都是有毒的.但是人懒 ...
- mysql编译好的简单安装
.创建mysql用户的账号 useradd mysql .下载mysql编译好的压缩包 .安装mysql yum -y install numactl libaio 安装依赖库 tar -zxvf m ...
- 导入别人的项目eclipse 出现乱码 该如何处理
- C#的app.Config文件中设置,可以选择执行环境(左配置,有程序),app.Config中的appSettings首字母必须小写,符合源码要求
- truncate和delete命令的优缺点和原理解析,适用场合
1.相同点:truncate和delete只删除数据不删除表的结构2.不同点:truncate.drop是数据库定义语言,操作立即生效,元数据不会放到rollback segement中,不能回滚,操 ...
- windows 多网卡路由设置
1.问题 windows操作系统,双网卡连接内外网,配置路由使内网请求走内网网卡,外网请求走外网网卡 2.网络参数 内网网卡 IP:172.22.25.152 mask: 255.255.255.0 ...
- JAVA常用集合
List: ArrayList: 基于动态数组的有序集合.优点:可以根据索引index下标访问List中的元素,访问速度快:缺点是访问和修改中间位置的元素时慢(数组尾部插入元素以外). LinkedL ...
- Maven 项目 启动时 解决3 字节的 UTF-8 序列的字节 3 无效
"org.activiti.bpmn.exceptions.XMLException: 3 字节的 UTF-8 序列的字节 3 无效." Maven 项目启动时,由于读XML配置文 ...
- 挑选队友 (生成函数 + FFT + 分治)
链接:https://www.nowcoder.com/acm/contest/133/D来源:牛客网 题目描述 Applese打开了m个QQ群,向群友们发出了组队的邀请.作为网红选手,Applese ...