热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

总结下js排序算法和乱序算法

其实本人最怕的就是算法,大学算法课就感觉老师在讲天书,而且对于前端来说,算法在实际的应用中实在是很有限。毕竟算法要依靠大量的数据为基础才能发挥出算法的效率,就浏览器那性能,..

  其实本人最怕的就是算法,大学算法课就感觉老师在讲天书,而且对于前端来说,算法在实际的应用中实在是很有限。毕竟算法要依靠大量的数据为基础才能发挥出算法的效率,就浏览器那性能,......是吧,退一万步说,真的有人把这大量的数据处理业务放到前端,那我只能说这是团队和架构师的失职,不说页面应用能不能加载出来,等你靠前端算出来,用户早就跑了。所以,就目前而言,绝大部分的算法使用场景都不在前端,就那么些数据量放在那,前端使用算法除了加重代码逻辑没有更多的好处。当然话又说回来了,我也知道这是个好东西,所以我也会去了解一点。这里就不说什么高深的算法了,先总结下相对简单的排序算法吧,以下均为js实现。

排序算法

1.TimSort算法

  看到这个算法名,大多数前端er都不知道这是什么算法吧,但是我要是说这个是实现大名鼎鼎v8引擎的sort()的核心算法,你们就应该恍然大悟了吧。旧版的排序sort()原理大家应该很熟悉了,数组长度小于10用插入排序,否则使用快速排序。旧版的排序sort()源码地址:https://github.com/v8/v8/blob/5.9.221/src/js/array.js#L996

  而新版的排序sort()源码使用Torque语言编写,源码备注是基于python的某一版本TimSort算法实现的,大致原理是归并排序和插入排序的混合排序算法:针对现实中需要排序的数据分析看,大多数据通常是有部分已经排好序的数据块,Timsort 称这些已经排好序(不管是升序还是降序)的数据块为 “run”。又因为在合并序列的时候,如果run的数量等于或者略小于2的幂次方的时候,效率是最高的,所以run要有一定的约束,于是根据序列长度定义了一个minrun,如果原始的run小于minrun的长度,用插入排序扩充run,最后将这些run用归并排序合并序列,得到最后的run就是排序后的结果。有兴趣的可以了解下源码,反正我看不懂,说到这,突然开始怀念旧版源码了。新版的排序sort()源码地址:https://github.com/v8/v8/blob/master/third_party/v8/builtins/array-sort.tq。

  大多数前端排序场景用这个方法就已经足够了,默认排序顺序(没有携带参数)是在将元素转换为字符串后,然后比较按照它们的UTF-16字符编码的顺序进行排序,如果要比较数字的话,就需要传入参数compareFunction。

使用代码短小精悍,如下:

var arr = [11,8,5,6,3,10,7,8,2];
function compareNumbers(a,b){
return a - b;
}
console.log(arr.sort());
//[10, 11, 2, 3, 5, 6, 7, 8, 8]
console.log(arr.sort(compareNumbers));//[2, 3, 5, 6, 7, 8, 8, 10, 11]

 

2.冒泡排序算法

  这个可以说是最基础的或是最常见的了吧,尤其是其形象的命名“冒泡”深入人心,顾名思义,经过不断的交换,最大的数会像水中的泡泡一样慢慢往上浮动,直至顶端。它会在未排序队列内,依次比较两个相邻的元素,把较大的元素放置于更靠顶端的位置。

其实现代码如下:

function bubbleSort(array) {
var length = array.length;
for(var i = length - 1; i > 0; i--) {
for(var j = 0; j ) {
if(array[j] > array[j+1]) {
var temp = array[j];
array[j]
= array[j+1];
array[j
+1] = temp;
}
}
console.log(array);
}
return array;
}


var arr = [11,8,5,6,3,10,7,8,2];
var result = bubbleSort(arr);

再放上一张形象的排序流程图,搭配上面的log图食用更佳。

 

3.选择排序算法

  在未排序队列内,选择其中最小的元素,然后和第一个元素进行位置互换,以此类推,直到所有元素排序完毕。

其实现代码如下:

function selectionSort(array) {
var length = array.length;
for(var i = 0; i ) {
var min = array[i];
var index = i;
for(var j = i + 1; j ) {
if(array[j] < min) {
min
= array[j];
index
= j;
}
}
if(index != i) {
var temp = array[i];
array[i]
= array[index];
array[index]
= temp;
}
console.log(array);
}
return array;
}

var arr = [11,8,5,6,3,10,7,8,2];
var result = selectionSort(arr);

排序流程图:

 

 4.插入排序算法

  这个算法打过斗地主或是跑的快的应该很熟悉,我这样说就明白了:就是抓到牌之后,我们会先展开牌,然后先把第一张放到左边(排序队列),然后把第二张(可以看为未排序队列第一张)从右边(未排序队列)再拿到左边去按从小到大进行排序,放到合适的位置。恩,形象的丫批。上文也说到了,这是旧版的排序sort()在数组长度小于10的情况下采用的排序算法。

其实现代码如下:

function insertionSort(array) {
var length = array.length;
for(var i = 0; i ) {
var insert = array[i+1];
var index = i + 1;
for(var j = i; j >= 0; j--) {
if(insert < array[j]) {
array[j
+1] = array[j];
index
= j;
}
}
array[index]
= insert;
console.log(array);
}
return array;
}

var arr = [11,8,5,6,3,10,7,8,2];
var result = insertionSort(arr);

排序流程图:

 

 5.希尔排序算法

  希尔排序算法是以其设计者shell的名字命名的排序算法,又称缩小增量排序,算是个插入排序算法的plus版本。它的本质是多个分组同时进行插入排序算法,所以会比插入排序算法更加高效。核心是,这个多个分组是按照队列的下标进行一定的增量分组。

其实现代码如下:

function shellSort(array) {
var length = array.length;
var gap = Math.round(length / 2);
while(gap > 0) {
for(var i = gap; i ) {
var insert = array[i];
var index = i;
for(var j = i; j >= 0; j-=gap) {
if(insert < array[j]) {
array[j
+gap] = array[j];
index
= j;
}
}
array[index]
= insert;
}
console.log(array);
gap
= Math.round(gap/2 - 0.1);
}
return array;
}

var arr = [11,8,5,6,3,10,7,8,2];
var result = shellSort(arr);

这个没有流程图,只能一步一步解释了。首先,gap就是增量,通过整个流程最后可以得出,gap依次取值为:5,2,1,0;

  一轮排序:根据增量5,对数组[11,8,5,6,3,10,7,8,2]而言就是11和10比较,8和7比较,5和8比较,6和2比较,最后剩下一个3不动;两两比较,大小互换位置,得到数组[10,7,5,2,3,11,8,8,6];

       注意,这里互换位置的时候是按照下标来互换的,这样光说看起来比较苍白无力,换成二维的吧

  二轮排序:此时增量为2,对数组[10,7,5,2,3,11,8,8,6]而言就是分为[10,5,3,8,6]比较,[7,2,11,8]比较,两组分别进行插入算法,大小互换位置,得到[3,5,6,8,10]和[2,7,8,11],即是[3,2,5,7,6,8,8,11,10];

        三轮排序:此时增量为1,对数组[3,2,5,7,6,8,8,11,10]直接进行插入排序算法,就得到了[2,3,5,6,7,8,8,10,11]。

        我去,突然想起来为啥不用excel来排版,算了,将就看吧。

 

6.归并排序算法

  一种典型的分而治之思想的算法应用,归并排序算法的实现有两种方法:



  1. 自上而下的递归

  2. 自下而上的迭代

  分治思想就是把一个大问题切分为若干个小问题,然后分别解决小问题,用这些小问题的答案来解释大问题。拿到归并算法来说,我觉得归并排序算法先是不断进行二分,然后最小单位的两个进行比较,形成若干个排序序列,最后再和二分法反着来将之前分开的若干已排列好的队列从最小单位开始慢慢比较头部然后合并回去。至于迭代和递归,以前看到知乎上一大佬这样形容,用电影来佐证,迭代是《明日边缘》,递归是《盗梦空间》,令人拍案叫绝,至今印象深刻。

其实现代码如下(采用第一种自上而下的递归方式):

function mergeSort(arr) {
var len = arr.length;
if(len <2) {
return arr;
}
var middle = Math.floor(len / 2),
left
= arr.slice(0, middle),
right
= arr.slice(middle);
return merge(mergeSort(left), mergeSort(right));
}
function merge(left, right)
{
var result = [];
while (left.length && right.length) {
if (left[0] <= right[0]) {
result.push(left.shift());
}
else {
result.push(right.shift());
}
}
while (left.length)
result.push(left.shift());
while (right.length)
result.push(right.shift());
console.log(result)
return result;
}
var arr = [11,8,5,6,3,10,7,8,2];
var result = mergeSort(arr);

 排序流程图;

 

7.快速排序算法

  如果说希尔排序算法是插入排序算法的plus版本,那么快速排序算法算是冒泡排序的plus版本了吧。其通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序队列。快速排序算法,字如其算法,听说是排序算法中数一数二的快了,效率非常高。上文也说到了,这是旧版的排序sort()在数组长度大于等于10的情况下采用的排序算法。

其实现代码如下:

function quickSort(array) {
var length = array.length;
if(length <= 1) {
return array;
}
else{
var smaller = [];
var bigger = [];
var base = [array[0]];
for(var i = 1; i ) {
if(array[i] <= base[0]) {
smaller.push(array[i]);
}
else{
bigger.push(array[i]);
}
}
console.log(smaller.concat(base.concat(bigger)));
return quickSort(smaller).concat(base.concat(quickSort(bigger)));
}
}


var arr = [11,8,5,6,3,10,7,8,2];
var result = quickSort(arr);

排序流程图(这个流程图是按照array[i]

是不是还是看不懂?再上一个图,这下看懂了吧,就是逼着你站队,以每轮的第一个元素为基准,你比它大就站到右边队伍去,比它小就站到左边队伍去,直到最后每个队伍都只剩自己孤单一元素的时候,排序就完成了。(这个图是按照array[i] <= base[0]分析的,主要是注意数组[11,8,5,6,3,10,7,8,2]中两个元素8的站位问题)

 

8.其他的排序算法

  其实还有其他的排序算法,像什么堆排序、计数排序、基数排序、桶排序,这里就不展开了,我也不会,手动狗头。

 

乱序算法

  说了辣么多排序算法,再来点乱序算法,先说点题外话,肯定很多人觉得利用sort方法就能实现乱序,其实现代码如下:

function randomSort() {
return Math.random()-0.5;
}
var arr = [1,2,3,4,5,6,7,8,9];
var result = arr.sort(randomSort);

  以前我也是这么认为的,但是之后看到一篇文章说,Math.random()是个伪随机,于是我去官网看了下api:https://developer.mozilla.org/zh-CN/docs/Web/Javascript/Reference/Global_Objects/Math/random

 也就是说random其实是以初始种子作为基准,一般都是默认取时间戳为种子,然后进行一系列固定的算法最终得到一个介于0和1之间的浮点数,也就是说只要知道种子的值,最后的结果是可以复刻的。最后我还是不死心,想自己测试下,于是就有了以下代码:

function test(num){
var arr = [0,1,2,3,4,5,6,7,8,9];
var total=[0,0,0,0,0,0,0,0,0,0];
var result=[];
for(var i=0;i){
var tempArr=[...arr].sort(randomSort);
for(var j=0;j){
total[j]+=tempArr[j];
}
}
total.forEach(item
=>{
item
=parseFloat((item/num).toFixed(3));
result.push(item)
})
return result;
}
var num=1000;
var result=test(num);
console.log(result);

  先解释下,如果random是随机的,那么数组[0,1,2,3,4,5,6,7,8,9]经过n次之后的randomSort方法,那么最后每次每个位置上的值应该会无限趋同于平均值4.5,但是从上图我们可以看出,明显数值越大的出现的机率会更高,所以它确实是个伪随机数。所以,真正的乱序算法在下面。

1.Fisher–Yates(费舍尔耶茨)算法,洗牌算法

  恩,这个名字就很直接了,和插入排序算法可以搞一桌子了。洗牌的动作嘛我不说都很熟悉,就是拿出一坨牌随机插入牌堆的位置,不断重复几次顺序就乱了。

其实现代码如下:

function shuffle(array) {
for(var i = array.length-1; i >=0; i--) {
var randomIndex = Math.floor(Math.random()*(i+1));
var itemAtIndex = array[randomIndex];
array[randomIndex]
= array[i];
array[i]
= itemAtIndex;
console.log(array);
}
return array;
}
var arr = [2,3,5,6,7,8,8,10,11];
var result = shuffle(arr);

  最后把我写的测试方法修改一下,将var tempArr=[...arr].sort(randomSort);替换为var tempArr=shuffle([...arr]);得出的结果如下,大部分都接近于4.5,甚至在1000000次的运算中,几乎每个位置都等于4.5了。

 

2.随机抽取算法

  这个就和洗牌算法差不多了,异曲同工,看下代码就懂了。

其实现代码如下:

function randomlySelected(arr) {
var array= [];
while (arr.length) {
var randomIndex = Math.floor(Math.random()*arr.length);
array.push(arr[randomIndex]);
arr.splice(randomIndex,
1);
console.log(array);
}
return array;
}
var arr = [2,3,5,6,7,8,8,10,11];
var result = randomlySelected(arr);

 测试的结果也挺不错。

   不知不觉,就码了这么多字了,子曰:温故而知新,总结这些js排序算法和乱序算法,不仅是对自己过往认知的总结,也是对之前的模糊不清的地方进行了梳理,感觉又深刻了亿内内了呢,告辞。



推荐阅读
  • 一、Hadoop来历Hadoop的思想来源于Google在做搜索引擎的时候出现一个很大的问题就是这么多网页我如何才能以最快的速度来搜索到,由于这个问题Google发明 ... [详细]
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • [译]技术公司十年经验的职场生涯回顾
    本文是一位在技术公司工作十年的职场人士对自己职业生涯的总结回顾。她的职业规划与众不同,令人深思又有趣。其中涉及到的内容有机器学习、创新创业以及引用了女性主义者在TED演讲中的部分讲义。文章表达了对职业生涯的愿望和希望,认为人类有能力不断改善自己。 ... [详细]
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • 2018年人工智能大数据的爆发,学Java还是Python?
    本文介绍了2018年人工智能大数据的爆发以及学习Java和Python的相关知识。在人工智能和大数据时代,Java和Python这两门编程语言都很优秀且火爆。选择学习哪门语言要根据个人兴趣爱好来决定。Python是一门拥有简洁语法的高级编程语言,容易上手。其特色之一是强制使用空白符作为语句缩进,使得新手可以快速上手。目前,Python在人工智能领域有着广泛的应用。如果对Java、Python或大数据感兴趣,欢迎加入qq群458345782。 ... [详细]
  • 本文详细解析了JavaScript中相称性推断的知识点,包括严厉相称和宽松相称的区别,以及范例转换的规则。针对不同类型的范例值,如差别范例值、统一类的原始范例值和统一类的复合范例值,都给出了具体的比较方法。对于宽松相称的情况,也解释了原始范例值和对象之间的比较规则。通过本文的学习,读者可以更好地理解JavaScript中相称性推断的概念和应用。 ... [详细]
  • 本文介绍了数据库的存储结构及其重要性,强调了关系数据库范例中将逻辑存储与物理存储分开的必要性。通过逻辑结构和物理结构的分离,可以实现对物理存储的重新组织和数据库的迁移,而应用程序不会察觉到任何更改。文章还展示了Oracle数据库的逻辑结构和物理结构,并介绍了表空间的概念和作用。 ... [详细]
  • 目录实现效果:实现环境实现方法一:基本思路主要代码JavaScript代码总结方法二主要代码总结方法三基本思路主要代码JavaScriptHTML总结实 ... [详细]
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • javascript  – 概述在Firefox上无法正常工作
    我试图提出一些自定义大纲,以达到一些Web可访问性建议.但我不能用Firefox制作.这就是它在Chrome上的外观:而那个图标实际上是一个锚点.在Firefox上,它只概述了整个 ... [详细]
  • 如何用UE4制作2D游戏文档——计算篇
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了如何用UE4制作2D游戏文档——计算篇相关的知识,希望对你有一定的参考价值。 ... [详细]
  • 本文介绍了PhysioNet网站提供的生理信号处理工具箱WFDB Toolbox for Matlab的安装和使用方法。通过下载并添加到Matlab路径中或直接在Matlab中输入相关内容,即可完成安装。该工具箱提供了一系列函数,可以方便地处理生理信号数据。详细的安装和使用方法可以参考本文内容。 ... [详细]
  • 图解redis的持久化存储机制RDB和AOF的原理和优缺点
    本文通过图解的方式介绍了redis的持久化存储机制RDB和AOF的原理和优缺点。RDB是将redis内存中的数据保存为快照文件,恢复速度较快但不支持拉链式快照。AOF是将操作日志保存到磁盘,实时存储数据但恢复速度较慢。文章详细分析了两种机制的优缺点,帮助读者更好地理解redis的持久化存储策略。 ... [详细]
  • Voicewo在线语音识别转换jQuery插件的特点和示例
    本文介绍了一款名为Voicewo的在线语音识别转换jQuery插件,该插件具有快速、架构、风格、扩展和兼容等特点,适合在互联网应用中使用。同时还提供了一个快速示例供开发人员参考。 ... [详细]
  • 基于事件驱动的并发编程及其消息通信机制的同步与异步、阻塞与非阻塞、IO模型的分类
    本文介绍了基于事件驱动的并发编程中的消息通信机制,包括同步和异步的概念及其区别,阻塞和非阻塞的状态,以及IO模型的分类。同步阻塞IO、同步非阻塞IO、异步阻塞IO和异步非阻塞IO等不同的IO模型被详细解释。这些概念和模型对于理解并发编程中的消息通信和IO操作具有重要意义。 ... [详细]
author-avatar
小永远佳瞳_186
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有