继上一篇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. Python深拷贝和浅拷贝!

    在python中,对象赋值实际上是对象的引用.当创建一个对象,然后把它赋给另一个变量的时候,python并没有拷贝这个对象,而只是拷贝了这个对象的引用 一般有三种方法, alist=[1,2,3,[& ...

  2. 多个表左联,要返回全部的结果,解决不能用where的问题

    qb.leftJoin('info_student', 'grouping_class_student.studentId', 'info_student.id'); qb.leftJoin('gro ...

  3. $.isPlainObject

    // Support: IE<9 // Handle iteration over inherited properties before own properties. if ( !suppo ...

  4. php JS 导出表格特殊处理

    但是这样导出身份证号会变为科学计数: 解决方法就是: 我们了解一下excel从web页面上导出的原理.当我们把这些数据发送到客户端时,我们想让客户端程序(浏览器)以excel的格式读取 它,所以把mi ...

  5. 网址导航19A

    [导航] KIM主页 265导航 好866 [名站] 百度 网易 腾讯 新华 中新 凤凰 [新闻] 联合早报  南方周末  澎湃新闻 [系统] 宋永志 蒲公英 技术员 装机网 系统之家 [软件] 星愿 ...

  6. 20172306 2018-2019-2 《Java程序设计与数据结构》第六周学习总结

    20172306 2018-2019-2 <Java程序设计与数据结构>第六周学习总结 教材学习内容总结 概述(了解一下树的一些概念) 树是一种非线性结构.树由一个包含结点和边的集构成,其 ...

  7. 30. pt-upgrade

    用来测试新老版本数据库对sql的结果是否一致.两台开发测试数据库,一台是老版本,一台是新版本,来做比较,生产库别用. pt-upgrade h=192.168.100.101,P=3306,u=adm ...

  8. makefile与动态链接库案例分析——动态库链接动态库

    http://blog.csdn.net/huqinwei987/article/details/50517780 背景:效率考虑,要重用把服务器主备机方案,以库Libmdpha(高可用)的形式加进主 ...

  9. C语言闰年问题程序框图

    判定2000-2500年中的每一年是否为闰年,并将结果输出. 先分析闰年成立条件: 1)能被4整除,但不能被100整除的年份都是闰年: (2)能被400整除的年份是闰年: #include<st ...

  10. 问题:计算foldRight(1)(_-_) 与foldLeft(1)(_-_)值不一样

    List(1,2,3,4)问题:计算foldRight(1)(_-_) 与foldLeft(1)(_-_)值不一样首先看foldRight(1)(_-_)计算过程((( (1-1)-2)-3)-4) ...