用js實現快排

By Charles Stover | Dec 12, 2018

原文javascript

原理🤔

Quicksort經過從數組中選取一個元素並將其表示爲基準點,把數組中的全部其餘元素分爲兩類 - 它們小於或大於此基準點。java

而後把做爲這一輪排序結果的兩個數組(數組元素都小於基準點的數組和數組元素都大於基準點的數組)再進行相同的排序。即分別再選個基準點,而後基於基準點分紅兩個數組元素分別小於和大於基準點的數組。算法

最終,因爲最後數組中沒有元素或只有一個元素,所以不用再比較了。剩下的值都已經基於基準點排好序了。數組

(譯者:內容有刪減,說的有些囉嗦)less

如何實現

js的Array原型的sort方法使用另一種方法實現排序的,咱們不用這個實現快排。咱們本身建立一個方法,待排序的數組做爲參數,輸出排好序的數組。函數

const quickSort = (unsortedArray) => {
  const sortedArray = TODO(unsortedArray);
  return sortedArray;
};
複製代碼

因爲數組中項的「值」可能不是很明顯,咱們應該爲排序算法提供一個可選參數。在js中,字符串和數字會排好序的,可是對象不會。咱們要根據user對象的age字段給數組排序。ui

const defaultSortingAlgorithm = (a, b) => {
  if (a < b) {
    return -1;
  }
  if (a > b) {
    return 1;
  }
  return 0;
};

const quickSort = (
  unsortedArray,
  sortingAlgorithm = defaultSortingAlgorithm
) => {
  const sortedArray = TODO(unsortedArray);
  return sortedArray;
};
複製代碼

因爲咱們是不斷地重複找基準點,而後輸出全小於基準點和全大於基準點的數組的這個步驟。咱們但願用遞歸來實現,這樣能夠少寫代碼。this

你能夠隨便找個基準點:第一個、中間、最後一個、隨機一個。爲了簡單起見,咱們假設基準點的選取對時間複雜度沒有影響。我在本文中老是使用最後一個元素做爲基準點,由於要配合下圖的演示(圖中用的是最後一個元素,來源維基百科)。spa

數組基於基準點分紅兩個數組:小於基準點的數組放前面,大於基準點的數組放後面。最終,把基準點放在兩個數組的中間,重複以上步驟。code

爲了避免改變原數據,咱們建立了新數組。這不是必要的,可是是個好的習慣。

咱們建立recursiveSort做爲遞歸函數,它將遞歸子數組(從起始索引到結束索引),中途改變sortedArray數組的數據。整個數組是第一個傳遞給此遞歸函數的數組。

最後,返回排好序的數組。

recursiveSort函數有一個pivotValue變量來表示咱們的基準點,還有一個splitIndex變量來表示分隔小於和大於數組的索引。從概念上講,全部小於基準點的值都將小於splitIndex,而全部大於基準點的值都將大於splitIndex。splitIndex被初始化爲子數組的開頭,可是當咱們發現小於基準點時,咱們將相應地調整splitIndex。

咱們將循環遍歷全部值,將小於基準點的值移動到起始索引以前。

const quickSort = (
  unsortedArray,
  sortingAlgorithm = defaultSortingAlgorithm
) => {

  // Create a sortable array to return.
  const sortedArray = [ ...unsortedArray ];

  // Recursively sort sub-arrays.
  const recursiveSort = (start, end) => {

    // If this sub-array contains less than 2 elements, it's sorted.
    if (end - start < 1) {   /*譯者:經熱心觀衆提醒,這裏應該是小於1,而不是小於2*/
      return;
    }

    const pivotValue = sortedArray[end];
    let splitIndex = start;
    for (let i = start; i < end; i++) {
      const sort = sortingAlgorithm(sortedArray[i], pivotValue);

      // This value is less than the pivot value.
      if (sort === -1) {

        // If the element just to the right of the split index,
        //   isn't this element, swap them.
        if (splitIndex !== i) {
          const temp = sortedArray[splitIndex];
          sortedArray[splitIndex] = sortedArray[i];
          sortedArray[i] = temp;
        }

        // Move the split index to the right by one,
        //   denoting an increase in the less-than sub-array size.
        splitIndex++;
      }

      // Leave values that are greater than or equal to
      //   the pivot value where they are.
    }

    // Move the pivot value to between the split.
    sortedArray[end] = sortedArray[splitIndex];
    sortedArray[splitIndex] = pivotValue;

    // Recursively sort the less-than and greater-than arrays.
    recursiveSort(start, splitIndex - 1);
    recursiveSort(splitIndex + 1, end);
  };

  // Sort the entire array.
  recursiveSort(0, unsortedArray.length - 1);
  return sortedArray;
};
複製代碼

咱們將全部小於基準點的值移動到splitIndex指向的位置,其餘的值不動(默認狀況下,大於splitIndex,由於splitIndex從子數組的開頭開始)。

一旦子數組被排序好後,咱們將基準點放在中間,由於排序就是基於基準點排的,咱們知道它的位置。

左邊的全部值(從start到splitIndex - 1)都會被遞歸排序,而且右邊的全部值(從splitIndex + 1到end)也都會被遞歸排序。 splitIndex自己如今是基準點,再也不須要對其進行排序。

相關文章
相關標籤/搜索