【JS面試向】選擇排序、桶排序、氣泡排序和快速排序簡介

一顆賽艇?發表於2019-02-18

新年伊始,又到了金三銀四的時候了。面對前端越來越多的演算法面試題,我簡單的整理了一下幾種比較常見的陣列排序方式,分別介紹其基本原理和優劣勢。(ps:才疏學淺,希望大家可以在issues下面指出問題)


選擇排序

原理

選擇排序從陣列內遍歷出最大值,加入新陣列,將最大值從原陣列中刪除,重複上述操作,最後得出的新陣列就是一個從大到小排序的陣列了。

程式碼實現

/**
 * params {number[]} list
 * return {number[]}
 */
function sort(list) {
  list = [...list]; // 最好不要對元素組操作
  const newList = []; // 建立待返回的空陣列
  while(list.length) { // 當 list.length === 0 時,表示處理完畢
    let min = Infinity; // 設最小值無窮大 或者 等於 list 中的任意位置都可
    let minIndex; // 記錄下最小值下標
    list.forEach((el, index) => { // 對 list 迴圈,查詢當前 list 最小值
      if(el < min) {
        min = el;
        minIndex = index;
      }
    });
    newList.push(list[minIndex]); // 將 最小值下標 對應的值 push 進陣列
    list.splice(minIndex, 1); // 從list內刪除這個值
  }
  return newList
}
複製程式碼

優劣

優點:

  • 上手比較簡單,比較符合人的正常思路邏輯。

缺點:

  • 時間複雜度O(n^2),運算速度很慢,當陣列元素個數比較多時,時間增速驚人。

桶排序

原理

桶排序顧名思義,就是準備好一些“桶”,然後將待排序的陣列值一個個放入對應的“桶內”,全部元素放入”桶“後,然後展開”桶“就得到了排序完成的陣列了。比如:當前需要排序的陣列 [8, 3, 5, 9, 2, 3, 0, 8],我們可以準備一個長度為10的陣列,每一項的值為0,我們對需要排序的陣列開始便利,當我們遇到8時,我們將newList[8]內的0,加1,改成1;然後下一個3,我們將newList[3]內的0,加1,改成1...,處理完所有元素後,將newList便利輸出就得到了排序好的陣列了。當然了這裡只是簡單的對桶排序的介紹,真正的桶排序肯定比這個複雜。

程式碼實現

const list = [8, 3, 5, 9, 2, 3, 0, 8]; // 待排序陣列

/**
 * params {number[]} list
 * return {number[]}
 */
function sort(list) {
  const newList = Array.from({length: 10}).fill(0); // 建立 [0, 0, ..., 0] 的陣列,長度為10
  list.forEach(el => newList[el] += 1); // 把陣列元素記錄在 newList 上
  return newList.reduce((pre, el, index) => { // 展開陣列
    for(let i = el; i; i--) {
      pre.push(index)
    }
    return pre;
  }, [])
}
複製程式碼

優劣

優點:

  • 時間複雜度只有O(m+n),計算效率高

缺點:

  • 空間消耗比較大

  • 需要提前知道最大值,最小值


氣泡排序

原理

氣泡排序我先介紹說它的原理,你就明白它為什麼叫氣泡排序了。有一個待排序的陣列 [8, 3, 5, 9, 2, 3, 0, 8] ,需要由小到大排序。我們只需要把小的放在左邊,大的放右邊是不是就完成了排序呢?顯然是的。將第一個 8 與 第二位 3 比較,8 大於 3,所以我們把8往右放,即將 8 與 3 更換位置,更換後的陣列是 [3, 8, 5, 9, 2, 3, 0, 8] ,繼續比較改變後的陣列第二位 8 與 第三位 5 比較,8 大於 5,更換後的陣列是 [3, 5, 8, 9, 2, 3, 0, 8],重複這樣的操作,如果遇到相同或者當前數值小於後一位的則不需要改變,繼續比較下一位即可。所以看這 8 的移動軌跡,像不像是一個氣泡在往上冒,所以這個排序方法就叫氣泡排序。

程式碼實現

/**
 * params {number[]} list
 * return {number[]}
 */
function sort(list) {
  list = [...list];
  let length = list.length;
  while(length--) {
    for(let i = 0; i < length; i++) {
      const current = list[i];
      const next = list[i + 1];
      if(current > next) {
        [list[i], list[i + 1]] = [next, current];
      }
    }
  }
  return list;
}
複製程式碼

優劣

優點:

  • 沒啥優點吧,我不清楚哈,歡迎賜教。

缺點:

  • 時間複雜度O(n^2),計算慢。

快速排序

中心思想是用二分實現的快速排序,能夠很快的完成排序任務,也是我比較推薦的一種排序方式。

原理

快速排序的優點就是速度快,為什麼速度快呢?我先介紹一下快速排序的原理。選擇一個基準值,一般選擇陣列的一個值,遍歷陣列,大的放右邊,小的放左邊,一樣大的放中間(哈哈哈哈哈?),利用遞迴重複對大的陣列和小的陣列進行拆分,最後得出排序後的陣列。

程式碼實現

function quickSort(arr) {
  if(arr.length < 2) {
    return arr;
  } else {
    const pivot = arr[0]; // 基準值
    const pivotArr = []; // 一樣大的放中間
    const lowArr= []; // 小的放左邊
    const hightArr = []; // 大的放右邊
    arr.forEach(current => {
      if(current === pivot) pivotArr.push(current);
      else if(current > pivot) hightArr.push(current);
      else lowArr.push(current);
    })
    return quickSort(lowArr).concat(pivotArr).concat(quickSort(hightArr));
  }
}
複製程式碼

優劣

優點:

  • 速度快,O(n*log n)

缺點:

  • 感謝 @CringKong 的指點。快速排序的的平均時間複雜度是O(n * log n),但最糟情況的複雜度是O(n ^ 2),比如:[1, 2, 3, 4, 5] 這種有序陣列。快排的穩定性不如歸併排序(後續我在開坑簡介一下)。
  • 我沒想到,各位大佬有空指點。O(∩_∩)O謝謝

相關文章