冒泡排序 & 選擇排序 & 插入排序 & 希爾排序 JavaScript 實現

以前用 JavaScript 寫過 快速排序歸併排序,本文聊聊四個基礎排序算法。(本文默認排序結果都是從小到大)javascript

冒泡排序

冒泡排序每次循環結束會將最大的元素 "冒泡" 到最後一位。html

以 [1, 5, 2, 4, 3] 爲例,O(n^2) 的複雜度,總共外層循環 5 次,第一次循環結束後的結果是 [1, 2, 4, 3, 5]。 首先是 1 和 5 比較,1 <=5,不交換位置,而後 5 和 2 比較,5 > 2,交換位置,數組變爲 [1, 2, 5, 4, 3],而後 5 和 4 比較,交換位置,數組變爲 [1, 2, 4, 5, 3],最後 5 和 3 比較,交換位置,數組爲 [1, 2, 4, 3, 5],這個時候最大的元素 5 已經到了最後,整個交換過程當中大的元素就好像 "冒泡" 同樣冒出來。而後 [1, 2, 4, 3] 再進行一樣操做,以此類推。java

冒泡排序看起來不管最好狀況仍是最壞狀況,複雜度同樣,都是 O(n^2)。算法

function swap(array, a, b) {
  var tmp = array[a];
  array[a] = array[b];
  array[b] = tmp;
}

function bubbleSort(array) {
  var _array = array.concat();

  for (var i = 0, len = _array.length; i < len; i++)
    for (var j = 0; j < len - 1 - i; j++)
      if (_array[j] > _array[j + 1])
        swap(_array, j, j + 1);
  
  return _array;
}

var a = [1, 5, 2, 4, 3];
var ans = bubbleSort(a);
console.log(ans); // [1, 2, 3, 4, 5]

選擇排序

選擇排序每次循環會找到最值元素的下標,而後將該元素交換到最前面。因此選擇元素每次循環交換一次,不會像冒泡同樣屢次交換。shell

仍是以 [1, 5, 2, 4, 3] 爲例,第一次循環比較,默認最值下標爲 0,最值爲 1,接着分別和 5,2,4,3 比較,ok 比完,最值的下標仍是 0,那麼就不交換(也能夠看作 array[0] 和 array[0] 交換)。接着進行第二輪,是爲 [5, 2, 4, 3] 進行循環,以此類推。數組

和冒泡相比,選擇排序也是不管好壞狀況,複雜度都是 O(n^2),而效率應該比冒泡稍微好點,畢竟交換次數少了。code

function swap(array, a, b) {
  var tmp = array[a];
  array[a] = array[b];
  array[b] = tmp;
}

function selectionSort(array) {
  var _array = array.concat();

  for (var i = 0, len = _array.length; i < len; i++) {
    // 最值元素下標
    var index = i;

    for (var j = i + 1; j < len; j++) 
      if (_array[j] < _array[index])
        index = j;

    swap(_array, i, index);
  }

  return _array;
}

var a = [1, 5, 2, 4, 3];
var ans = selectionSort(a);
console.log(ans); // [1, 2, 3, 4, 5]

插入排序

插入排序會比前面兩種排序算法高效。它將數組分紅 "已排序" 和 "未排序" 兩部分,一開始的時候,"已排序" 的部分只有一個元素,而後將它後面一個元素從 "未排序" 部分插入 "已排序" 部分,從而 "已排序" 部分增長一個元素,"未排序" 部分減小一個元素。以此類推,完成所有排序。(摘自阮老師的博文 http://javascript.ruanyifeng.com/library/sorting.htmlhtm

仍是以 [1, 5, 2, 4, 3] 爲例,外層仍是須要循環 5 次,假設循環到第三次,到 2 這個元素,前面已經有序,是爲 [1, 5],咱們要將 2 插入,首先比較 5 和 2,交換,此時數組前三項爲 [1, 2, 5],再比較 1 和 2,ok,不用交換了,有序了,比較結束。再看 4,第一次比較後,交換,數組爲 [1, 2, 4, 5],而後 4 和 2 比較,ok,有序了,不用繼續比了,那麼 2 就不用和 1 比較了,這樣就大大節省了相鄰元素兩兩比較的次數。blog

和前二者相比,插入排序能減小比較次數,固然最壞狀況下仍是 O(n^2),可是和選擇排序相比,可能會多交換次數。排序

function insertionSort(array) {
  var _array = array.concat();

  for (var i = 0, len = _array.length; i < len; i++) {
    // 儲存當前位置的值
    var item = _array[i];
    
    // 和前面已經有序的部分,比較,交換
    for (j = i - 1; j > -1 && _array[j] > item; j--) 
      _array[j + 1] = _array[j];

    _array[j+1] = item;
  }

  return _array;
}


var a = [1, 5, 2, 4, 3];
var ans = insertionSort(a);
console.log(ans); // [1, 2, 3, 4, 5]

固然,真實生產環境中不可能用這三種排序方法,畢竟效率過低!不過必定要比較效率的話,我以爲是 插入排序 > 選擇排序 > 冒泡排序!

希爾排序

希爾排序是選擇排序的升級版,能夠說是分組插入排序,聽說複雜度達到 O(n^1.2)。

希爾排序是基於插入排序的如下兩點性質而提出改進方法的:

  • 插入排序在對幾乎已經排好序的數據操做時, 效率高, 便可以達到線性排序的效率
  • 但插入排序通常來講是低效的, 由於插入排序每次只能將數據移動一位

它是怎麼排序的呢?咱們設定一個變量叫作 gap,gap 小於數組長度,對於數組,咱們將距離 gap 的元素劃分爲一組,每組進行插入排序,gap 不斷變小,最後減爲 1,即完成希爾排序。

gap 如何取值?有個簡單一點的方法,第一次取值數組長度一半,而後再一半,再一半,最後爲 1。固然若是要求更高效率,能夠深刻研究下 gap 的取值。至於希爾排序爲何效率會比普通的插入排序高,這點不在本文探討範圍以內(實際上是我不知道),有興趣的能夠查閱相關資料。

// 希爾排序:先將整個待排序記錄序列分割成若干個子序列
// 在序列內分別進行直接插入排序,待整個序列基本有序時,
// 再對全體記錄進行一次直接插入排序
function shellSort(array){
  var len = array.length
    , gap = ~~(len >> 1); 

  // 克隆數組
  var result = array.concat();

  while (gap > 0) {
    for(var i = gap; i < len; i++) {

      var tmp = result[i];
      var j = i - gap;

      while (j >= 0 && tmp < result[j]) {
        result[j + gap] = result[j];
        j -= gap; 1
      }

      result[j + gap] = tmp;
    }

    gap = ~~(gap >> 1);
  }

  return result;
}


var a = [1, 5, 2, 4, 3];
var ans = shellSort(a);
console.log(ans); // [1, 2, 3, 4, 5]

Read More:

相關文章
相關標籤/搜索