最近自己总结了js常见的几种排序算法,并进行了简单的测试和对比。包括:冒泡排序,插入排序,选择排序,快速排序,归并排序等。
冒泡排序比较简单,就是从前往后依次比较相邻的两个值,如果前一个大于后一个值就交换位置,一趟之后最大的值就排在最后一位了,然后同理在排序剩下的n-1 ,n-2 …个数代码如下:
function bubbleSort (list) {
for (let i = 0; i < list.length; i++) {
for (let j = 0; j < list.length - i; j++) {
if (list[j] > list[j + 1]) {
let temp = list[j];
list[j] = list[j + 1];
list[j + 1] = temp;
}
}
}
return list;
}
选择排序的基本思路,将数组分为左侧的有序序列如:[a0,…ai],和右侧的无序待排序列[a[i+1],…an],每次将无序序列中选出一个最小值放入到有序序列的最后一个。代码如下:
function selectSort (list) {
for (let i = 0; i < list.length - 1; i++) {
for (let j = i + 1; j < list.length; j++) {
if (list[j] < list[i]) {
let temp = list[i];
list[i] = list[j];
list[j] = temp;
}
}
}
return list;
}
插入排序的思想也是将数组分为有序序列和无序序列两个部分,每次从无序序列中拿出第一个,然后在有序序列中比较插入到适当的位置,然后重复操作直到数列完全有序,代码如下:
function insertSort (list) {
for (let i = 1; i < list.length; i++) {
for (let j = i; j > 0; j--) {
if (list[j] < list[j - 1]) {
let temp = list[j];
list[j] = list[j - 1];
list[j - 1] = temp;
}
}
}
return list;
}
通常情况下快速排序可以称得上是效率最高的排序方法了。快排的基本思想是通过在数列中找到一个中间值,使小于中间值的值排在左边,大于中间值的排在右边,左边的任意一个值都要小于等于右边数列的值,然后再分别对左侧和右侧的数列进行分割以此类推,知道整个数列有序。代码如下:
function quickSort (list) {
function quick (list, start, end) {
let lower = start,
height = end,
key = Math.floor ((start + end) / 2),
powkey = list[key];
list[key] = list[lower];
list[lower] = powkey;
while (height > lower) {
while (list[height] >= powkey && height > lower) {
height--;
}
if (lower < height) {
list[lower++] = list[height];
}
while (list[lower] <= powkey && height > lower) {
lower++;
}
if (lower < height) {
list[height--] = list[lower];
}
}
list[lower] = powkey;
if (start + 1 < lower) {
quick (list, start, lower - 1);
}
if (lower + 1 < end) {
quick (list, lower + 1, end);
}
}
quick (list, 0, list.length - 1);
return list;
}
上面代码就是整个快排实现过程。首先调用内部quick方法,传入数组,开始和结束位置,然后在方法内部定义一个高位指针heigh和一个地位指针lower,
然后设置中间值,这里我们去中,间的值(通常会取第一个,但是如果数组本身就有序,那么每次分出来的两个数列基本就是左边为0,右边是整个数组,这种情况会使快排的效率极具下降,所以我去中间值,然后同第一个交换位置,再把第一个做为关键值进行比较)。首先从高位比较,如果高位的值大于中间值powkey,则位置不变,如果小于powkey说明应该位于中间值的左侧,这时包这个高位的值放到低位lower,然后lower++ 判断低位的值是否大于powkey,如果大于说明应该位于右侧,然后把该值移动到刚才空余出来的高位height(上面把高位的值移动到低位了),然后再从高位比较重复之前过程直到lower和height重合。重合的位置重新设置为powkey。一次分割完成,判断左右两侧的数组长度是否大于1,大于1继续递归,否则不用分割。
归并排序的思维也是对数组进行一分为二分割,不同的是归并排序不是按照关键值进行分割,而是从中间将数组一分为二,然后不断分割形成一颗二叉树,例:[6,5,7,2,5, 9]如图:
从二叉树最底层向上有序合并,如,5和7合并为[5,7], 5和9合并[5,9]然后,在向上合并,6 和[5,7]合并为[5,6,7], 2和[5,9]合并为[2,5,9],一直合并到最顶端合并为一个数组。
代码如下:
function mergeSort (list) {
function merge (left, right) {
let list = [];
while (left[0] && right[0]) {
if (left[0] < right[0]) {
list.push (left.shift ());
} else {
list.push (right.shift ());
}
}
list = list.concat (left).concat (right);
return list;
}
function sort (arr) {
if (arr.length === 1) {
return arr;
}
let mid = Math.floor ((arr.length + 1) / 2);
return merge (sort (arr.slice (0, mid)), sort (arr.slice (mid)));
}
return sort (list);
}
首先调用内部sort,传入数组,如果长度为1则返回,否则将数组一分为二,递归调用merge,传入的两个参数分别为分别递归调用sort进行拆分,知道长度为1即二叉树最底层,然后进行merge,在merger中分别对两个 数组第一个值进行比较,最小的放入新的数组中,然后返回一个新的有序的数组,即返回二叉树的上一级。在上一级中再次进行数组合并,返回一个新的有序数组,知道合并为同一个。
先写到这里,稍后更新。