继上一篇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 基础排序的实现(二)的更多相关文章

  1. JavaScript基础知识总结(二)

    JavaScript语法 二.数据类型 程序把这些量.值分为几大类,每一类分别叫什么名称,有什么特点,就叫数据类型. 1.字符串(string) 字符串由零个或多个字符构成,字符包括字母,数字,标点符 ...

  2. javascript基础学习(十二)

    javascript之BOM 学习要点: 屏幕对象 History对象 Location对象 一.屏幕对象 Screen对象是一个由javascript自动创建的对象,该对象的主要作用是描述客户端的显 ...

  3. JavaScript 基础排序的实现(一)

    作为一个有追求的前端,忙里偷闲(闲得发慌)地复习了一下基础的排序算法,以此文留念. 本篇主要记录O(n²)复杂度的基础算法O(nlogn)的算法将在下次有空(闲得发慌)时更新 在记录时发现Es6语法中 ...

  4. JavaScript基础入门教程(二)

    说明 前一篇博客介绍了js以及一些关于js基本类型的简单知识,本篇博客将详细介绍js的基础类型,捎带介绍对象类型,更详细的对象类型的说明将后续再讲. js中类型的说明 js中的类型分为基本类型和对象类 ...

  5. Javascript基础系列之(二)变量

    javascript 中变量通过var关键字(variable)来声明的. var school = "beijingyizhong" 也可以通过var 关键字给变量多个值. va ...

  6. JavaScript基础笔记(十二)Ajax

    Ajax 一.XMLHttpRequest对象 一)XHR用法 var xhr = new XMLHttpRequest(); //open()方法,参数一:发送方法,参数二:请求的URL,参数三:是 ...

  7. javaScript 基础知识汇总(二)

    1.运算符 术语或者叫法:一元运算符.二元运算符.运算元(参数) let x=0; x=5+2; //5和2为运算元,“+” 为二元运算符: x=-x; //"-" 为一元运算符 ...

  8. javascript基础总结之实例(二)

    div的显示和隐藏 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://w ...

  9. 原生javascript 基础动画函数封装(二)

    <!DOCTYPE html> <html> <head> <title></title> <style type="tex ...

随机推荐

  1. server 打开失败

    server:An unexpected exception was thrown. 当server服务器遇到这样遇到不能料想的错误导致打开失败的情况下,我们可以找到一个com.genuitec.ec ...

  2. iptables命令提取总结,包含扩展模块<取自朱双印博客>

    以下内容只是一些命令相关的,以朱双印博客中的iptables的教程提取出来的.纯粹只是命令的总结,如果需要看理论的知识,建议去看朱老师的博客,目前还没有看到写得比这个好的了. <http://w ...

  3. .net数据库实现Excel的导入与导出

    .net数据库实现Excel的导入与导出 参考路径:https://www.cnblogs.com/splendidme/archive/2012/01/05/2313314.html 1.defau ...

  4. 数据库基础 RDBMS、NoSQL

  5. h5 调起ios数字键盘的坑,限制特殊字符输入方案

    最近有个需求是利率只允许输入数字和小数点,用以下 <input type="number" pattern="[0-9]*"> 在ios会调起数字键 ...

  6. go语言的条件语句和循环语句

    一,条件语句 常见的就是if语句: 单支条件语句:     if   条件 :执行语句   (注,如果是没有逻辑运算符连接的话,是可以不需要括号的,也可以加上括号,如:if (条件):执行语句) 双支 ...

  7. Hibernate框架:CRM练习--保存客户

    crm:customer ralation manager 客户关系管理系统 一.准备 1.创建web项目 2.导包 最终为: 3.引入静态页面 将文件复制放入项目的WebContent目录下面: 4 ...

  8. python3 爬取搜狗微信的文章

    目标地址:http://weixin.sogou.com/weixin? 这个地址是搜狗微信的文章搜索,可以搜索到微信的文章,而我们目标就是这些文章内容 这个url经过测试,当我们没登陆微信只能看到1 ...

  9. 使用proxyTable解决vue里的跨域问题

    由于没有跨域的接口,所以,用8080端口请求8081端口,来模拟跨域.跨域会出现下面报错. 1.找到config文件夹下index.js,在proxyTable对象里复制以下代码: proxyTabl ...

  10. java操作docker示例(docker-java)

    1.首先要修改docker服务器的 /usr/lib/systemd/system/docker.service,加入紫色框的配置 2.下载docker-java 的github源码 git clon ...