JavaScript 版各大排序算法

近来看到了许多公司都在预备来岁的练习校招,虽然离三月份另有一段时间,觉得已能够预备了。在网上看了一些排序算法和数组去重操纵,觉得都写的很好,心血来潮,也来写一写。

《JavaScript 版各大排序算法》

排序算法的设想和完成

说到排序,之前在做百度前端学院的题目的时刻,也碰到过,并把它整理到 github 上。这是一个可视化的排序展现,支撑冒泡、插进去和挑选排序,详细运用先 随机增加 40 个,然后点排序,就能够看到可视化的效果。

引荐一下,HTML5 Canvas Demo: Sorting Algorithms,这里另有个可视化的排序博客,各大排序算法的完成都维妙维肖。

javascript 写排序算法也比较奇葩,主假如参数的题目,比方 javascript 算法函数能够扔给 Array 原型:Array.prototype.sort = function,也能够直接写个函数带参数:function sort(array){},在我看来,哪一种要领都一样,须要注重的是兼容性的题目,假如能够斟酌对一切可遍历对象都能排序(比方 arguments),才大法好。

好了,直接入主题了(下面的排序均是从小到大的递次)。

插进去排序

插进去排序是一种基础排序,它的基础思绪是构建有序序列,关于未排序的数据,在已排序的基础上,从右向左(或许二分查找)挑选位置插进去,维基百科-插进去排序

function insert_sort(input){
  var i, j, temp;
  for(i = 1; i < input.length; i++){
    temp = input[i];
    for(j = i-1; j >= 0 && input[j] > temp; j--)
      input[j+1] = input[j];
    input[j+1] = temp;
  }
  return input;
}

假如以比较次数和挪动次数来权衡算法的效力,最好状况下,比较 n-1 次,挪动 0 次,最坏状况,比较 n*(n-1)/2 次,挪动 n*(n-1)/2 次。

二分插进去排序

思绪基础同上,只是在查找插进去位置的时刻,不是顺次查找,而是采纳二分法:

function bin_insert_sort(input){
  var i, j, low, high, mid, temp;
  for(i = 1; i < input.length; i++){
    temp = input[i];
    high = i - 1;
    low = 0;
    while(low <= high){
      mid = parseInt((low + high) / 2);
      if(temp < input[mid]){
        high = mid - 1;
      }else{
        low = mid + 1;
      }
    }
    // low 位置就是要插进去的位置
    for(j = i-1; j >= low; j--)
      input[j+1] = input[j];
    input[low] = temp;
  }
  return input;
}

希尔排序

希尔排序实际上是加强版的插进去排序,就是在本来插进去排序的基础上,加入了步长,本来插进去排序的步长是 1,而且步长差别,效力也有差别,挑选一个适宜的步长也很主要。而且,希尔排序的末了一步,也必定是步长为 1 的插进去排序,只不过此时全部排序已基础稳固。维基百科-希尔排序

function shell_sort(input){
  var gap, i, j, temp;
  gap = input.length >> 1;
  while(gap > 0){
    for (i = gap; i < input.length; i++) {
      temp = input[i];
      for (j = i - gap; j >= 0 && input[j] > temp; j -= gap)
        input[j + gap] = input[j];
      input[j + gap] = temp;
    }
    gap = gap >> 1;
  }
  return input;
}

挑选排序

挑选排序的事情道理:首先在未排序序列中找到最小(大)元素,存放到排序序列的肇端位置,然后,再从盈余未排序元素中继承寻觅最小(大)元素,然后放到已排序序列的末端。以此类推,直到一切元素均排序终了。维基百科-冒泡排序

function select_sort(input){
  var i, j, min, temp;
  for(i = 0; i < input.length - 1; i++){
    min = i;
    for(j = i + 1; j < input.length; j++){
      if(input[min] > input[j])
        min = j;
    }
    temp = input[min];
    input[min] = input[i];
    input[i] = temp;
  }
  return input;
}

挑选排序在最好状况下,也要比较 n*(n-1)/2,挪动 n-1 次(这里能够加个推断,挪动 0 次),最差状况下,比较 n*(n-1)/2 次,挪动 n-1 次。一切最好,最坏状况下,比较次数是一样的。

冒泡排序

冒泡排序的基础道理:关于带排序列,它会屡次遍历序列,每次都邑比较相邻的两个元素,若递次相反,即交换它们,维基百科-冒泡排序

function bubble_sort(input){
  var i, j, temp, flag;
  for(i = 0; i < input.length - 1; i++){
    flag = true;
    for(j = 0; j < input.length - i; j++){
      if(input[j] > input[j + 1]){
        temp = input[j];
        input[j] = input[j + 1];
        input[j + 1] = temp;
        flag = false;
      }
    }
    if(flag)
      // 提前结束
      break;
  }
  return input;
}

有 flag 时,最好状况比较 n-1 次,挪动 0 次,最坏状况,比较 n*(n-1)/2 次,交换 n*(n-1)/2。

快排

记得我一个同学去百度口试,百度口试官上来就让他手写了一个快排,可见对快排的控制很主要呀,而且快排明白起来也不容易。

维基百科-快排。快排的基础思绪就是挑选一个元素,然后根据与这个元素的比较,将大于这个元素的都拿到右侧,小于这个元素的都拿到左侧,并找到这个元素的位置,这个元素的摆布双方递归。

function quick_sort(input){
  function sort(start, end){
    if(start >= end){
      return;
    }
    var mid = partition(start, end);
    sort(start, mid - 1);
    sort(mid + 1, end);
  }
  function partition(start, end){
    var left = start, right = end, key = input[start], temp;
    while(left < right){
      while(left < right && input[right] >= key){
        right --;
      }
      input[left] = input[right];
      while(left < right && input[left] <= key){
        left ++;
      }
      input[right] = input[left];
    }
    input[left] = key;
    return left;
  }
  // main here
  sort(0, input.length - 1);
  return input;
}

partition 函数就是来找对应的 mid,sort 函数用来排序。

关于快排的优化,能够从以下几个方面来斟酌:

  1. partition 函数的尖兵(比较值)除了 start 之外,用其他位置(比方中位数)是不是可行;

  2. 当 start 和 end 间距很小的时刻,改用其他高效算法

  3. 另有就是优化递归。

实在呢,上面的这个算法,并不属于 JavaScript 版本,而更像 C 版本的,重在让人明白快排,下面是 JS 版的快排,来体验下 JS 的诱人特征吧:

// javascript 版
function quick_sort(input) {
  var len = input.length;
  if (len <= 1)
    return input.slice(0);
  var left = [];
  var right = [];
  // 基准函数
  var mid = [input[0]];
  for (var i = 1; i < len; i++)
    if (input[i] < mid[0])
      left.push(input[i]);
    else
      right.push(input[i]);
  return quick_sort(left).concat(mid.concat(quick_sort(right)));
};

这个 JS 版快排也比较好懂,找到谁人基准(这里是第一个元素 input[0])以后,遍历,把小于基准的放到左侧,大于基准的放到右侧,然后返回拼接数组。

合并排序

在进修分治算法时,典范的一个例子就是合并。维基百科-合并排序。思绪就是先分后和,依旧是递归。

function merge_sort(input){
  function merge(left, right){
    var temp = [];
    var i = 0, j = 0;
    while(i < left.length && j < right.length){
      if(left[i] < right[j]){
        temp.push(left[i]);
        i++;
      }else{
        temp.push(right[j]);
        j++;
      }
    }
    if(i < left.length){
      temp = temp.concat(left.slice(i));
    }
    if(j < right.length){
      temp = temp.concat(right.slice(j));
    }
    return temp;
  }
  if(input.length <=1){
    return input;
  }
  var mid = parseInt(input.length / 2);
  return merge(merge_sort(input.slice(0, mid)), merge_sort(input.slice(mid)))
}

一样,以上合并仍然是相似 C 言语版本,JavaScript 版本以下:

// javascript 版
function merge_sort(input) {
  var merge = function(left, right) {
    var final = [];
    while (left.length && right.length)
      final.push(left[0] <= right[0] ? left.shift() : right.shift());
    return final.concat(left.concat(right));
  };
  var len = input.length;
  if (len < 2) return input;
  var mid = len / 2;
  return merge(merge_sort(input.slice(0, parseInt(mid))), merge_sort(input.slice(parseInt(mid))));
};

数组的一系列操纵大大优化排序的历程。

堆排序

堆排序(Heapsort)是指应用堆这类数据构造所设想的一种排序算法。聚集是一个近似完整二叉树的构造,并同时满足聚集的性子:即子结点的键值或索引老是小于(或许大于)它的父节点。维基百科-堆排序

实在,关于堆排序,只需切记几个操纵就能够,比方找到末了一个父节点,怎样找到子节点(初始为 0),怎样竖立一个最大堆。

function heap_sort(input){
  var arr = input.slice(0);
  function swap(i, j) {
    var tmp = arr[i];
    arr[i] = arr[j];
    arr[j] = tmp;
  }
  // 上推操纵
  function max_heapify(start, end) {
    var dad = start;
    var son = dad * 2 + 1;
    if (son >= end)
      return;
    if (son + 1 < end && arr[son] < arr[son + 1])
      son++;
    if (arr[dad] <= arr[son]) {
      swap(dad, son);
      max_heapify(son, end);
    }
  }

  var len = arr.length;
  // 竖立一个最大堆
  for (var i = Math.floor(len / 2) - 1; i >= 0; i--)
    max_heapify(i, len);
  for (var i = len - 1; i > 0; i--) {
    swap(0, i);
    max_heapify(0, i);
  }

  return arr;
};

堆排序的历程大抵以下:先天生一个最大堆,然后将根节点(最大元素)与末了一个元素交换,然后把剩下的 n-1 元素再次天生最大堆,交换,天生…

总结

那末题目来了,究竟这些算法写的对不对,不然写个测试剧本来尝尝:

// 两种排序算法
var test = function(sort1, sort2){
  var arr1 = [], arr2 = [];
  // 随机天生 100 个 1~100 随机数
  function random_arr(a1, a2){
    var tmp;
    for(var i = 0; i < 100; i++){
      tmp = parseInt(Math.random()*100) + 1;
      a1.push(tmp);
      a2.push(tmp);
    }
  }

  var flag = true;
  for(var i = 0; i < 100; i++){
    random_arr(arr1, arr2);
    // 比较排序算法的效果
    if(sort1(arr1).toString() != sort2(arr2).toString()){
      flag = false;
      break;
    }
    arr1 = arr2 = [];
  }
  return flag ? "Ok!" : "Error!"
}

console.log(test(insert_sort, merge_sort)); //"Ok!"

假如已知插进去排序是准确的状况下,就能够考证合并排序是不是准确了。共勉!

参考

维基百科 排序搜刮
聊一聊排序算法
秒杀9种排序算法(JavaScript版)
排序图解:js排序算法完成

欢迎来我的博客交换

    原文作者:songjz
    原文地址: https://segmentfault.com/a/1190000008138236
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞