JS十大排序算法总结

489 阅读16分钟

js排序算法

一.冒泡排序

1.规则:依次比较相邻的两个数,如果不符合排序规则,则调换两个数的位置。这样一遍比较下来,能够保证最大(或最小)的数排在最后一位。再对最后一位以外的数组,重复前面的过程,直至全部排序完成。

2.详解:

var arr = [9, 8, 7, 6, 5, 4];
0轮:
9, 8, 7, 6, 5, 4
8, 9, 7, 6, 5, 4
8, 7, 9, 6, 5, 4
8, 7, 6, 9, 5, 4
8, 7, 6, 5, 9, 4
8, 7, 6, 5, 4, 9
1轮:
8, 7, 6, 5, 4
7, 8, 6, 5, 4
7, 6, 8, 5, 4
7, 6, 5, 8, 4
7, 6, 5, 4, 8
2轮:
7, 6, 5, 4
6, 7, 5, 4
6, 5, 7, 4
6, 5, 4, 7
3轮:
6, 5, 4
5, 6, 4
5, 4, 6
4轮:
5, 4
4, 5
总结:
比较的轮数 = 数组长度 - 1;
每一轮比较的次数 = 数组长度 - 当前轮数      

3.代码实现

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;
} 

二:选择排序

1. 规则:选择排序(Selection Sort)与冒泡排序类似,也是依次对相邻的数进行两两比较。不同之处在于,它不是每比较一次就调换位置,而是一轮比较完毕,找到最大值(或最小值)之后,将其放在正确的位置,其他数的位置不变。

2.详解:

		第一轮:
            9, 8, 7, 6, 5, 4
            8, 9, 7, 6, 5, 4
            7, 9, 8, 6, 5, 4
            6, 9, 8, 7, 5, 4
            5, 9, 8, 7, 6, 4
            4, 9, 8, 7, 6, 5
            一号位 比较出最小的数为4

        第二轮:
               9, 8, 7, 6, 5
               8, 9, 7, 6, 5
               7, 9, 8, 6, 5
               6, 9, 8, 7, 5
               5, 9, 8, 7, 6
               二号位 比较出最小的数为5

        第三轮:
                  9, 8, 7, 6
                  8, 9, 7, 6
                  7, 9, 8, 6
                  6, 9, 8, 7
                   三号位 比较出最小的数为6
        第四轮:
                     9, 8, 7
                     8, 9, 7
                     7, 9, 8
                     四号位 比较出最小的数为7
        第五轮:
                         8, 9
                         四号位 比较出最小的数为8

        总结:
        比较的轮数  =  数组长度  - 1
        每一轮比较次数 = 数组长度 - 当前轮次        

3.代码实现

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.规则:插入排序(insertion sort)比前面两种排序方法都更有效率。它将数组分成“已排序”和“未排序”两部分,一开始的时候,“已排序”的部分只有一个元素,然后将它后面一个元素从“未排序”部分插入“已排序”部分,从而“已排序”部分增加一个元素,“未排序”部分减少一个元素。以此类推,完成全部排序。

2.详解

 var arr = [3, 2, 4, 5, 1];
1.将数组分成[3]和[2, 4, 5, 1]两部分,前者是已排序的,后者是未排序的。
2.取出未排序部分的第一个元素“2”,与已排序部分最后一个元素“3”比较,因为2小于3,所以2排在3前面,整个数组变成[2, 3]和[4, 5, 1]两部分。
3.取出未排序部分的第一个元素“4”,与已排序部分最后一个元素“3”比较,因为4大于3,所以4排在3后面,整个数组变成[2, 3, 4]和[5, 1]两部分。
4.取出未排序部分的第一个元素“5”,与已排序部分最后一个元素“4”比较,因为5大于4,所以5排在4后面,整个数组变成[2, 3, 4, 5]和[1]两部分。
5.取出未排序部分的第一个元素“1”,与已排序部分最后一个元素“5”比较,因为1小于5,所以再与前一个元素“4”比较;因为1小于4,再与前一个元素“3”比较;因为1小于3,再与前一个元素“2”比较;因为小于1小于2,所以“1”排在2的前面,整个数组变成[1, 2, 3, 4, 5]。
0轮:
    i=1;
    key=2;
    j=0;
    比较3跟2的值 如果3>2 array[1]=3;j=-1 跳出循环
    array[0] = 2
    [2,3,4,5,1]
1轮:
    i=2;
    key=4;
    j=1;
    比较3跟4的值 3<4不符合条件跳出循环
    array[2]= 4
    [2,3,4,5,1]
2轮:
    i=3;
    key=5;
    j=2;
    比较4跟5的值 4<5不符合条件跳出循环
	 array[3]= 5
    [2,3,4,5,1]
3轮:
	i=4;
	key=1;
	j=3;
	比较5跟1的值 5>1 array[4]=5;j=2;
	比较4跟1的值 4>1 array[3]=4;j=1;
	比较3跟1的值 3>1 array[2]=3;j=0;
	比较2跟1的值 2>1 array[1]=2;j=-1;不符合条件 跳出循环
	array[1] = 1;
	排序完成 [1,2,3,4,5]  

3.代码实现

 function insertionSort(array) {
    if (Object.prototype.toString.call(array).slice(8, -1) === 'Array') {
      for (var i = 1; i < array.length; i++) {
        var key = array[i];
        var j = i - 1;
        while (j >= 0 && array[j] > key) {
          array[j + 1] = array[j];
          j--;
        }
        array[j + 1] = key;
      }
      return array;
    } else {
      return 'array is not an Array!';
    }
  }         		  

四:希尔排序

1.规则:希尔排序是按一定的间隔对数列进行分组,然后在每一个分组中做插入排序;随后逐次缩小间隔,在每一个分组中做插入排序...直到间隔等于1,做一次插入排序后结束。

2.详解

var arr = [2,5,10,7,1,3];
1.先把数组拆分成4个间隔的 2-1 5-3	
2.2-1 5-3	进行大小比较 互换位置 [1,3,10,7,2,5]
3.第二次修改gap间隔的值 改为Math.floor(gap/3)也就是1 下面流程就跟快速排序一样的流程
0轮:
    gap = 4;
    i=4;
    temp=1;
    j=0;j>=0;arr[j]>temp符合条件
    arr[4] = 2; [2,5,10,7,2,3];
    j-=gap;j=-4不符合条件
    arr[0]=1; [1,5,10,7,2,3] //目前2跟1互换了位置
1轮:
    i=5;
    temp=3;
    j=1;j>=0;arr[j]>temp符合条件
    arr[5] = 5; [1,5,10,7,2,5];
    j-=gap;j=-3不符合条件
    arr[1]=3; [1,3,10,7,2,5] //目前5跟3互换了位置
2轮:
    i=6;不符合i<len的条件 gap=Math.floor(gap/3) = 1;
    i=1;
    temp = 3;
    j=0;j>=0;arr[j]<temp不符合条件跳出循环
    arr[1] = 3;
3轮:
	i=2;
    temp = 10;
    j=1;j>=0;arr[j]<temp不符合条件跳出循环
    arr[2] = 10; 
 4轮:
	i=3;
    temp = 7;
    j=2;j>=0;arr[j]>temp符合条件
    arr[3] = 10; [1,3,10,7,2,5]
    j-=gap;j=1;
    j>=0;arr[j]<temp不符合条件跳出循环  
    arr[2] = 7 [1,3,7,10,2,5]
    以此类推

3.代码实现

function shellSort(arr) {
    var len = arr.length, temp, gap = 1;
    while(gap < len/3) {          //动态定义间隔序列
      gap = gap*3+1;
    }
    for (gap; gap> 0; gap = Math.floor(gap/3)) {
      for (var i = gap; i < len; i++) {
        temp = arr[i];
        for (var j = i-gap; j >= 0 && arr[j]> temp; j-=gap) {
          arr[j+gap] = arr[j];
        }
        arr[j+gap] = temp;
      }
    }
    return arr;
  }

五:归并排序

1.规则:归并排序采用的是分治的思想,首先是“分”,将一个数组反复二分为两个小数组,直到每个数组只有一个元素;其次是“治”,从最小数组开始,两两按大小顺序合并,直到并为原始数组大小,下面是图解:

avatar

2.详解:

var arr = [3,44,38,5,47];
0轮:
    middel = 2;
    left = [3,44];
    right = [38,5,47];
    首先先执行mergeSort(left)先拆分左数组
    middle = 1;
    left = [3],
    right = [44];
    在递归拆封左数组 mergeSort(left) 因为此刻len=1;len<2;直接return [3];
    在递归拆封右数组 mergeSort(right) 因为此刻len=1;len<2;直接return [44];
    直接执行merge([3,44]) 执行完结果返回[3,44];

1轮:
    执行mergeSort(right)先拆分右数组
    middle=1;
    left = [38],
    right = [5,47];
    在递归拆封左数组 mergeSort(left) 因为此刻len=1;len<2;直接return [38];
    在递归拆封右数组 
    middle = 1;
    left = [5];
    right = [47];
    mergeSort(left) 因为此刻len=1;len<2;直接return [5];
    mergeSort(right) 因为此刻len=1;len<2;直接return [47];
    直接执行merge([5,47]) 执行完结果返回[5,47];
2轮:
	然后进行合并排序 merge[38,5,47];
	执行结果为[5,38,47];
3轮:
	然后进行合并排序 merge[3,44,5,38,47];
	执行结果为[3,5,38,44,47];
	完成排序

3.代码实现:

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;
}

六:快速排序

1.规则:

  • (1)在数据集之中,选择一个元素作为"基准"(pivot)。
  • (2)所有小于"基准"的元素,都移到"基准"的左边;所有大于"基准"的元素,都移到"基准"的右边。
  • (3)对"基准"左边和右边的两个子集,不断重复第一步和第二步,直到所有子集只剩下一个元素为止。

2.代码实现:

var quickSort = function (arr) {
if (arr.length <= 1) {
  return arr;
}
var pivotIndex = Math.floor(arr.length / 2);//中间元素的位置
var pivot = arr.splice(pivotIndex, 1)[0];
var left = [];
var right = [];
for (var i = 0; i < arr.length; i++) {
  if (arr[i] < pivot) {
    left.push(arr[i]);
  } else {
    right.push(arr[i]);
  }
}
return quickSort(left).concat([pivot], quickSort(right));

};

七:计数排序

1.规则:

  • 第一步:找出原数组中元素值最大的,记为max。
  • 第二步:创建一个新数组count,其长度是max加1,其元素默认值都为0。
  • 第三步:遍历原数组中的元素,以原数组中的元素作为count数组的索引,以原数组中的元素出现次数作为count数组的元素值。
  • 第四步:创建结果数组result,起始索引index。
  • 第五步:遍历count数组,找出其中元素值大于0的元素,将其对应的索引作为元素值填充到result数组中去,每处理一次,count中的该元素值减1,直到该元素值不大于0,依次处理count中剩下的元素。
  • 第六步:返回结果数组result。

2.代码实现

function countingSort(iArr, max) {
    var n = iArr.length;
    var oArr = [];
    // 创建长度max的数组,填充0
    var C = [];
    for (var i = 0; i <= max; i++) {
      C[i] = 0; //[0,0,0,0,0,0,0]
    }
    // 遍历输入数组,填充C
    for (var j = 0; j < n; j++) {
      C[iArr[j]]++; //[0,1,1,2,1,0,1]
    }
    // 遍历C,输出数组
    for (var k = 0; k <= max; k++) {
      // 按顺序将值推入输出数组,并在比较后将对应标志位减1 注意是先push后减
      while (C[k]-- > 0) {
        oArr.push(k); //[-1,-1,-1,-1,-1,-1,-1]
      }
    }
    return oArr;
  }

  var arr = [3, 2, 1, 6, 4 , 3];
  console.log(countingSort(arr, Math.max.apply(null, arr)));

八:桶排序(blog.csdn.net/m0_37477061…)

1.规则

  • 设置固定空桶数
  • 将数据放到对应的空桶中
  • 将每个不为空的桶进行排序
  • 拼接不为空的桶中的数据,得到结果

2.详解

假设一组数据(20长度)为[63,157,189,51,101,47,141,121,157,156,194,117,98,139,67,133,181,13,28,109] 现在需要按5个分桶,进行桶排序,实现步骤如下:

  • 找到数组中的最大值194和最小值13,然后根据桶数为5,计算出每个桶中的数据范围为(194-13+1)/5=36.4
  • 遍历原始数据,(以第一个数据63为例)先找到该数据对应的桶序列Math.floor(63 - 13) / 36.4) =1,然后将该数据放入序列为1的桶中(从0开始算)
  • 当向同一个序列的桶中第二次插入数据时,判断桶中已存在的数字与新插入的数字的大小,按从左到右,从小打大的顺序插入。如第一个桶已经有了63,再插入51,67后,桶中的排序为(51,63,67) 一般通过链表来存放桶中数据,但js中可以使用数组来模拟
  • 全部数据装桶完毕后,按序列,从小到大合并所有非空的桶(如0,1,2,3,4桶)
  • 合并完之后就是已经排完序的数据

avatar

3.代码实现

var bucketSort = function(arr, bucketCount) {
    if (arr.length <= 1) {
        return arr;
    }
    bucketCount = bucketCount || 10;
    //初始化桶
    var len = arr.length,
    buckets = [],
    result = [],
    max = arr[0],
    min = arr[0];
    for (var i = 1; i < len; i++) {
        min = min <= arr[i] ? min: arr[i];
        max = max >= arr[i] ? max: arr[i];
    }
    //求出每一个桶的数值范围
    var space = (max - min + 1) / bucketCount;
    //将数值装入桶中
    for (var i = 0; i < len; i++) {
        //找到相应的桶序列
        var index = Math.floor((arr[i] - min) / space);
        //判断是否桶中已经有数值
        if (buckets[index]) {
            //数组从小到大排列
            var bucket = buckets[index];
            var k = bucket.length - 1;
            while (k >= 0 && buckets[index][k] > arr[i]) {
                buckets[index][k + 1] = buckets[index][k];
                k--
            }
            buckets[index][k + 1] = arr[i];
        } else {
            //新增数值入桶,暂时用数组模拟链表
            buckets[index] = [];
            buckets[index].push(arr[i]);
        }
    }
    //开始合并数组
    var n = 0;
    while (n < bucketCount) {
        if (buckets[n]) {
            result = result.concat(buckets[n]);
        }
        n++;
    }
    return result;
};
//开始排序
arr = bucketSort(arr, self.bucketCount);

九:基数排序

1.规则:

  • 首先确定基数为10,数组的长度也就是10.每个数都会在这10个数中寻找自己的位置。
  • 不同于BinSort会直接将数放在数组的下标处,如将 [34] 放在下标为34的位置,即a[34] = 34;基数排序是将34分开为3和4,第一轮排序根据最末位放在数组的下标4处,第二轮排序根据倒数第二位放在数组的下标3处,然后遍历数组即可。

2.详解:

		0轮:
		按照个位数进行排序;
		i = 0;
		dev = 1;
		mod = 10;
		bucket 算出来的是个位数
		couter[0] = [50];
		couter[1] = [];
		couter[2] = [2];
		couter[3] = [3];
		.....
		couter[9] = [19];
		接下来的循环 按照个位数进行排序的结果 [50,2,3,4,44,5,15....]
	        1轮:
	    	按照十位数进行排序:
	    	i = 1;
	    	dev = 10;
	    	mod = 100;
	    	bucket 算出来的是十位数 如果没有十位数补0
	    	根据第一轮排序的结果进行处理 按照十位数添加数组 得到结果
		如下图	    

avatar

3.代码实现:

	/**
	 * 基数排序适用于:
	 *  (1)数据范围较小,建议在小于1000
	 *  (2)每个数值都要大于等于0
	 * @author xiazdong
	 * @param  arr 待排序数组
	 * @param  maxDigit 最大位数
	 */
	//LSD Radix Sort
	
	function radixSort(arr, maxDigit) {
	    var mod = 10;
	    var dev = 1;
	    var counter = [];
	    for (var i = 0; i < maxDigit; i++, dev *= 10, mod *= 10) {
	        for(var j = 0; j < arr.length; j++) {
	            var bucket = parseInt((arr[j] % mod) / dev);
	            if(counter[bucket]== null) {
	                counter[bucket] = [];
	            }
	            counter[bucket].push(arr[j]);
	        }
	        var pos = 0;
	        for(var j = 0; j < counter.length; j++) {
	            var value = null;
	            if(counter[j]!=null) {
	                while ((value = counter[j].shift()) != null) {
	                      arr[pos++] = value;
	                }
	          }
	        }
	    }
	    return arr;
	}
	var arr = [3, 44, 38, 5, 47];
	console.log(radixSort(arr,2)); //[3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]

十:堆排序

1.规则:

  • 将初始二叉树转化为大顶堆(heapify)(实质是从第一个非叶子结点开始,从下至上,从右至左,对每一个非叶子结点做shiftDown操作),此时根结点为最大值,将其与最后一个结点交换。
  • 除开最后一个结点,将其余节点组成的新堆转化为大顶堆(实质上是对根节点做shiftDown操作),此时根结点为次最大值,将其与最后一个结点交换。
  • 重复步骤2,直到堆中元素个数为1(或其对应数组的长度为1),排序完成。

2.详解:

参考链接:segmentfault.com/a/119000001… www.cnblogs.com/chengxiao/p…

3.代码实现:

	  // 交换两个节点
	  function swap(A, i, j) {
	    let temp = A[i];
	    A[i] = A[j];
	    A[j] = temp;
	  }
	
	  // 将 i 结点以下的堆整理为大顶堆,注意这一步实现的基础实际上是:
	  // 假设 结点 i 以下的子堆已经是一个大顶堆,shiftDown函数实现的
	  // 功能是实际上是:找到 结点 i 在包括结点 i 的堆中的正确位置。后面
	  // 将写一个 for 循环,从第一个非叶子结点开始,对每一个非叶子结点
	  // 都执行 shiftDown操作,所以就满足了结点 i 以下的子堆已经是一大
	  //顶堆
	  function shiftDown(A, i, length) {
	    let temp = A[i]; // 当前父节点
	// j<length 的目的是对结点 i 以下的结点全部做顺序调整
	    for(let j = 2*i+1; j<length; j = 2*j+1) {
	      temp = A[i];  // 将 A[i] 取出,整个过程相当于找到 A[i] 应处于的位置
	      if(j+1 < length && A[j] < A[j+1]) {
	        j++;   // 找到两个孩子中较大的一个,再与父节点比较
	      }
	      if(temp < A[j]) {
	        swap(A, i, j) // 如果父节点小于子节点:交换;否则跳出
	        i = j;  // 交换后,temp 的下标变为 j
	      } else {
	        break;
	      }
	    }
	  }
	
	  // 堆排序
	  function heapSort(A) {
	    // 初始化大顶堆,从第一个非叶子结点开始
	    for(let i = Math.floor(A.length/2-1); i>=0; i--) {
	      shiftDown(A, i, A.length);
	    }
	    // 排序,每一次for循环找出一个当前最大值,数组长度减一
	    for(let i = Math.floor(A.length-1); i>0; i--) {
	      swap(A, 0, i); // 根节点与最后一个节点交换
	      shiftDown(A, 0, i); // 从根节点开始调整,并且最后一个结点已经为当
	      // 前最大值,不需要再参与比较,所以第三个参数
	      // 为 i,即比较到最后一个结点前一个即可
	    }
	  }
	
	  let Arr = [4, 6, 8, 5, 9, 1, 2, 5, 3, 2];
	  heapSort(Arr);		
	  
	  //另一种
	  /*方法说明:堆排序
		@param  array 待排序数组*/
		function heapSort(array) {
		    console.time('堆排序耗时');
		    if (Object.prototype.toString.call(array).slice(8, -1) === 'Array') {
		        //建堆
		        var heapSize = array.length, temp;
		        for (var i = Math.floor(heapSize / 2) - 1; i >= 0; i--) {
		            heapify(array, i, heapSize);
		        }
		
		        //堆排序
		        for (var j = heapSize - 1; j >= 1; j--) {
		            temp = array[0];
		            array[0] = array[j];
		            array[j] = temp;
		            heapify(array, 0, --heapSize);
		        }
		        console.timeEnd('堆排序耗时');
		        return array;
		    } else {
		        return 'array is not an Array!';
		    }
		}
		/*方法说明:维护堆的性质
		@param  arr 数组
		@param  x   数组下标
		@param  len 堆大小*/
		function heapify(arr, x, len) {
		    if (Object.prototype.toString.call(arr).slice(8, -1) === 'Array' && typeof x === 'number') {
		        var l = 2 * x + 1, r = 2 * x + 2, largest = x, temp;
		        if (l < len && arr[l] > arr[largest]) {
		            largest = l;
		        }
		        if (r < len && arr[r] > arr[largest]) {
		            largest = r;
		        }
		        if (largest != x) {
		            temp = arr[x];
		            arr[x] = arr[largest];
		            arr[largest] = temp;
		            heapify(arr, largest, len);
		        }
		    } else {
		        return 'arr is not an Array or x is not a number!';
		    }
		}
		var arr=[91,60,96,13,35,65,46,65,10,30,20,31,77,81,22];
		console.log(heapSort(arr));//[10, 13, 20, 22, 30, 31, 35, 46, 60, 65, 65, 77, 81, 91, 96]