前端排序方法

做编程,排序是个必然的需求。前端也不例外,虽然不多,但是你肯定会遇到。

不过说到排序,最容易想到的就是冒泡排序,选择排序,插入排序了。

冒泡排序

依次比较相邻的两个元素,如果后一个小于前一个,则交换,这样从头到尾一次,就将最大的放到了末尾。

从头到尾再来一次,由于每进行一轮,最后的都已经是最大的了,因此后一轮需要比较次数可以比上一次少一个。虽然你还是可以让他从头到尾来比较,但是后面的比较是没有意义的无用功,为了效率,你应该对代码进行优化。

图片演示如下:

代码实现:

function bubbleSort(arr) {

   var len = arr.length;

   for (var i = 0; i < len - 1; i++) {

       for (var j = 0; j < len - 1 - i; j++) {

           if (arr[j] > arr[j+1]) {        // 相邻元素两两对比

               var temp = arr[j+1];        // 元素交换

               arr[j+1] = arr[j];

               arr[j] = temp;

           }

       }

   }

   return arr;

}

选择排序

选择排序我觉得是最简单的了,大一学VB的时候,就只记住了这个排序方法,原理非常简单:每次都找一个最大或者最小的排在开始即可。

首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置 
再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。 
重复第二步,直到所有元素均排序完毕。 
动图演示:

代码演示:

function selectionSort(arr) {

   var len = arr.length;

   var minIndex, temp;

   for (var i = 0; i < len - 1; i++) {

       minIndex = i;

       for (var j = i + 1; j < len; j++) {

           if (arr[j] < arr[minIndex]) {     // 寻找最小的数

               minIndex = j;                 // 将最小数的索引保存

           }

       }

       temp = arr[i];

       arr[i] = arr[minIndex];

       arr[minIndex] = temp;

   }

   return arr;

}

插入排序

插入排序也比较简单。就像打扑克一样,依次将拿到的元素插入到正确的位置即可。

1.将第一待排序序列第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列。 
2.从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置。(如果待插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等元素的后面。)

动图演示:

代码示例:

function insertionSort(arr) {

   var len = arr.length;

   var preIndex, current;

   for (var i = 1; i < len; i++) {

       preIndex = i - 1;

       current = arr[i];

       while(preIndex >= 0 && arr[preIndex] > current) {

           arr[preIndex+1] = arr[preIndex];

           preIndex--;

       }

       arr[preIndex+1] = current;

   }

   return arr;

}

简单的代价是低效

上面三种都是非常简单的排序方法,简单的同时呢,效率也会比较低,还是拿这本书里的对比图来说明:

时间复杂度都高达O(n^2),而它们后面的一些排序算法时间复杂度基本都只有O(n log n)。

我的强迫症又犯了,我想要高效率一点的排序方法。

归并排序

简单把这本书的内容过了一遍,当时就理解了这个归并排序,因此这里就谈一下这个归并排序吧。

基本原理是分治法,就是分开并且递归来排序。

步骤如下:

1.申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列; 
2.设定两个指针,最初位置分别为两个已经排序序列的起始位置; 
3.比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置; 
4.重复步骤 3 直到某一指针达到序列尾; 
5.将另一序列剩下的所有元素直接复制到合并序列尾。

动图演示:

代码示例:

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());

 

   return result;

}

既然是个爱折腾的人,折腾了总得看看效果吧。

效率测试

由于我学这个来进行排序不是对简单数组,数组内都是对象,要对对象的某个属性进行排序,还要考虑升降序。

因此我的代码实现如下:

/**

* [归并排序]

* @param  {[Array]} arr   [要排序的数组]

* @param  {[String]} prop  [排序字段,用于数组成员是对象时,按照其某个属性进行排序,简单数组直接排序忽略此参数]

* @param  {[String]} order [排序方式 省略或asc为升序 否则降序]

* @return {[Array]}       [排序后数组,新数组,并非在原数组上的修改]

*/

var mergeSort = (function() {

   // 合并

   var _merge = function(left, right, prop) {

       var result = [];

 

       // 对数组内成员的某个属性排序

       if (prop) {

           while (left.length && right.length) {

               if (left[0][prop] <= right[0][prop]) {

                   result.push(left.shift());

               } else {

                   result.push(right.shift());

               }

           }

       } else {

           // 数组成员直接排序

           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());

 

       return result;

   };

 

   var _mergeSort = function(arr, prop) { // 采用自上而下的递归方法

       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, prop), _mergeSort(right, prop), prop);

   };

 

   return function(arr, prop, order) {

       var result = _mergeSort(arr, prop);

       if (!order || order.toLowerCase() === ‘asc‘) {

           // 升序

           return result;

       } else {

           // 降序

           var _ = [];

           result.forEach(function(item) {

               _.unshift(item);

           });

           return _;

       }

   };

})();

需要对哪个属性进行排序是不确定,可以随意指定,因此写成了参数。有由于不想让这些东西在每次循环都进行判断,因此代码有点冗余。

关于降序的问题,也没有加入参数中,而是简单的升序后再逆序输出。原因是不想让每次循环递归里都去判断条件,所以简单处理了。

下面就是见证效率的时候了,一段数据模拟:

var getData = function() {

   return Mock.mock({

       "list|1000": [{

           name: ‘@cname‘,

           age: ‘@integer(0,500)‘

       }]

   }).list;

};

上面使用Mock进行了模拟数据,关于Mock : http://mockjs.com/

实际测试来啦:

// 效率测试

var arr = getData();

 

console.time(‘归并排序‘);

mergeSort(arr, ‘age‘);

console.timeEnd(‘归并排序‘);

 

console.time(‘冒泡排序‘);

for (var i = 0, l = arr.length; i < l - 1; ++i) {

   var temp;

   for (var j = 0; j < l - i - 1; ++j) {

       if (arr[j].age > arr[j + 1].age) {

           temp = arr[j + 1];

           arr[j + 1] = arr[j];

           arr[j] = temp;

       }

   }

}

console.timeEnd(‘冒泡排序‘);

进行了五次,效果如下:

// 归并排序: 6.592ms

// 冒泡排序: 25.959ms

 

// 归并排序: 1.334ms

// 冒泡排序: 20.078ms

 

// 归并排序: 1.085ms

// 冒泡排序: 16.420ms

 

// 归并排序: 1.200ms

// 冒泡排序: 16.574ms

 

// 归并排序: 2.593ms

// 冒泡排序: 12.653ms

最低4倍,最高近16倍的效率之差还是比较满意的。

虽然1000条数据让前端排序的可能性不大,但是几十上百条的情况还是有的。另外由于node,JavaScript也能运行的服务端了,这个效率的提升也还是有用武之地的。

一点疑问

归并排序里面使用了递归,在《数据结构与算法 JavaScript 描述》中,作者给出了自下而上的迭代方法。但是对于递归法,作者却认为:

However, it is not possible to do so in JavaScript, as the recursion goes too deep for the language to handle.

然而,在 JavaScript 中这种方式不太可行,因为这个算法的递归深度对它来讲太深了。 
gitbook上这本书的作者对此有疑问,我也有疑问。

归并中虽然用了递归,但是他是放在return后的呀。关于在renturn后的递归是有尾递归优化的呀。

关于尾递归优化是指:本来外层函数内部再调用一个函数的话,由于外层函数需要等待内层函数返回后才能返回结果,进入内层函数后,外层函数的信息,内存中是必须记住的,也就是调用堆栈。而内部函数放在return关键字后,就表示外层函数到此也就结束了,进入内层函数后,没有必要再记住外层函数内的所有信息。

上面是我的理解的描述,不知道算不算准确。chrome下已经可以开启尾递归优化的功能了,我觉得这个递归是不该影响他在JavaScript下的使用的。

时间: 2024-10-02 07:43:21

前端排序方法的相关文章

【JavaScript】利用sort()函数与文件碎片实现表格的前端排序,兼容IE6原生态

表格排序在网页的应用也很多,尤其是一些信息系统输出一个密密麻麻的表格给人看,客户肯定会提出表格排序的要求.很多人定式思维地认为表格的排序一定要通过数据库后端进行交互,使用带order by asc/desc的语句去实现,然后再利用ajax似乎很完美似得.其实根本就不用与数据库打交道.在前端给出任意一个表格,都能够利用sort()函数与文件碎片实现表格的前端排序.在jquery里面是有一个advanceTable的插件做这件事,但是这个插件相当不好的是什么呢?与平常的插件一样,代码写得天花龙凤,没

js前端排序

javascript 做前端排序 <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>前端排序</title> <script src="js/jquery-1.6.2.min.js"></script> </head> <body> <script type="

【学习ios之路:Objective-C】OC中常用的系统排序方法

①.OC中常用排序方法: 1).不可变数组 - (NSArray *)sortedArrayUsingSelector:(SEL)comparator; - (NSArray *)sortedArrayUsingComparator:(NSComparator)cmptr; 2)可变数组 - (void)sortUsingSelector:(SEL)comparator; - (void)sortUsingComparator:(NSComparator)cmptr; 3).字典排序 - (NS

Java实现几种常见排序方法

转自:http://www.cnblogs.com/sevenyuan/archive/2009/12/04/1616897.html 日常操作中常见的排序方法有:冒泡排序.快速排序.选择排序.插入排序.希尔排序,甚至还有基数排序.鸡尾酒排序.桶排序.鸽巢排序.归并排序等. 冒泡排序是一种简单的排序算法.它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来.走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成.这个算法的名字由来是因为越小的元素会经

Java几种常用的实现排序方法

import java.util.Random; public class NumberSort{ /** * 私有构造方法,禁止实例化 */ private NumberSort(){ super(); } /** * 冒泡排序 * 比较相邻的元素.如果第一个比第二个大,就叫唤他们两个位置. * 对每一组相邻的元素作同样的工作,从开始的第一对到结束后的最后一对,这样剩下的最后一个应该是最大的数. * 针对所有元素重复以上操作,除了最后一个. * 持续对越来越少的数进行以上的操作,直到没有任何一

简单排序方法

排序: 排序(Sort)是计算机程序设计中的一种重要操作,也是日常生活中经常遇到的问题.例如,字典中的单词是以字母的顺序排列,否则,使用起来非常困难.同样,存储在计算机中的数据的次序,对于处理这些数据的算法的速度和简便性而言,也具有非常深远的意义. 基本概念: 排序是把一个记录(在排序中把数据元素称为记录)集合或序列重新排列成按记录的某个数据项值递增(或递减)的序列. 下表是一个学生成绩表,其中某个学生记录包括学号.姓名及计算机文化基础.C 语言.数据结构等课程的成绩和总成绩等数据项.在排序时,

c#实现几种排序方法

插入排序 1.简介 插入排序(Insertion Sort)的算法描述是一种简单直观的排序算法.它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入.插入排序在实现上,通常采用in-place排序(即只需用到O(1)的额外空间的排序),因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间. 2.算法描述 一般来说,插入排序都采用in-place在数组上实现.具体算法描述如下:1.从第一个元素开始,该元素可以认为已经被排序2.

数组重排序方法

今天在复习的过程中,发现自己对数组的重排序方法sort()一直存在一个误区,重新捋顺,在此记录... 数组中存在两个可以直接用来重排序的方法:reverse()和sort(),reverse()方法就是反转数组项的顺序.在默认情况下,sort()方法按升序排列数组项,在这个过程中sort方法会调用每个数组项的toString()转型方法,然后比较得到的字符串,然后确定排序.之前一直没有注意到这一点,一直以为调用sort()方法过后会直接对一个数组进行正确排序,其实并不是这样,而是对得到的字符串进

JavaScript高级程序设计--对象,数组(栈方法,队列方法,重排序方法,迭代方法)

1.使用对象字面量定义对象 var person={}; 使用这种方式创建对象时,实际上不会调用Object构造函数. 开发人员更喜欢对象字面量的语法. 2.有时候需要传递大量可选参数的情形时,一般来讲使用对象字面量来封装多个可选参数. 3.对象属性的点表示法与方括号表示法的区别 (1)功能上:两者没区别 (2)但是方括号的有点是可以通过变量来访问属性 例如: var person={ name:"Nic" } 点表示法:person.name 方括号表示法:var prop=“nam