Array.sort 算法原理(插入排序\快速排序in-place實現)

javascript Array.sort 原理實現

js 提供了 sort 方法,方便對數組進行排序,然而不一樣引擎對 jssort 方法解析可能存在差別,本文基於 v8 引擎對排序 sort 的實現,分析其使用的 插入排序快速排序javascript

本文不致力於分析 v8 引擎源碼,僅分析內部使用的兩種算法html


插入排序

下面是一張動圖演示。java

插入排序動圖

基本思想

從左往右遍歷數組,每次將遍歷的項插入到前面的已經排序好的有序數組中,經過構建有序序列,對於未排序數據,在已排序序列中從後向前掃描,找到相應位置並插入。git

算法步驟

  1. 默認第一項已經排序
  2. 從數組第二項開始遍歷,取出當前遍歷的新元素。
  3. 從當前項前一個往前查找,若是查找的項比新元素大,就向後移動一位
  4. 找到新元素在有序序列中的位置(查找的項比新元素小,那新元素由於位於該元素後,也就是空出的空位中)放入新元素
  5. 繼續遍歷,重複2-4.

代碼實現

function InsertionSort(arr, from = 0, to = arr.length) {
  // 從數組第二項遍歷
  for (var i = from + 1; i < to; i++) {
    // 取出新元素
    var element = arr[i];
    // 重新元素位置向前查找
    for (var j = i - 1; j >= from; j--) {
      // 緩存查找的項
      var tmp = arr[j];
      // 計算是不是須要插入的位置
      // 此處可修改插入邏輯,正序倒序
      var order = tmp - element;
      if (order > 0) {
        // 不是插入位置,查找項後移
        arr[j + 1] = tmp;
      } else {
        // 是插入位置,退出循環,插入元素
        break;
      }
    }
    // 退出循環插入元素
    arr[j + 1] = element;
  }
};
複製代碼

快速排序

快速排序,又稱劃分交換排序。以分治法爲策略實現的快速排序算法。這裏主要實現 in-place 思想的快速排序github

算法思想

快速排序的基本思想:經過一趟排序將待排記錄分隔成獨立的兩部分,其中一部分記錄的關鍵字均比另外一部分的關鍵字小,則可分別對這兩部分記錄繼續進行排序,以達到整個序列有序。算法

算法步驟

  1. 選基準:在數據結構中選擇一個元素做爲基準(pivot)
  2. 劃分區:參照基準元素值的大小,劃分無序區,全部小於基準元素的數據放入一個區間,全部大於基準元素的數據放入另外一區間,分區操做結束後,基準元素所處的位置就是最終排序後它應該所處的位置
  3. 遞歸:對初次劃分出來的兩個無序區間,遞歸調用第 1步和第 2步的算法,直到全部無序區間都只剩下一個元素爲止。

代碼實現

這裏根據分區操做的實現方法分爲下面兩種實現方式數組


  • 挖坑把基準元素往中間夾逼方式
    1. 取出第一項做爲基準元素(可理解爲第一項空出坑位)
    2. 從後往前找小於基準元素的元素,填入坑位,空出後面的坑位。
    3. 從前日後找大於基準元素的元素,填入後方空位,空出前面坑位
    4. 重複2-3
    5. 當位置正好到中間時結束,把基準元素放入最後空出的空位
/** * https://www.runoob.com/w3cnote/quick-sort.html * 快速排序算法 * @param {*} arr * @param {*} left * @param {*} right */

function quickSort(arr, left = 0, right = arr.length - 1) {

  function partition(arr, left, right) {
    const povit = arr[left];
    while (left < right) {
      while (left < right && arr[right] >= povit) {
        right--;
      }
      if (left < right) {
        arr[left] = arr[right]; //將s[right]填到s[left]中,s[right]就造成了一個新的坑
        left++;
      }

      while (left < right && arr[left] < povit) {
        left++;
      }

      if (left < right) {
        arr[right] = arr[left]; //將s[right]填到s[left]中,s[right]就造成了一個新的坑
        right--;
      }
    }

    arr[left] = povit;
    return left;
  }


  if (left < right) {
    // 分治
    const partitionIndex = partition(arr, left, right);
    quickSort(arr, left, partitionIndex);
    quickSort(arr, partitionIndex + 1, right);
  }
  return arr;
}
複製代碼

  • 順序遍歷分區思想
    1. 取最右邊的元素爲基準
    2. 記錄已經比對後小於基準元素的數組最後位置
    3. 從左向右遍歷數組
    4. 小於基準則移動到比對後的最後位置,並更新最後位置
    5. 遍歷完成把基準元素添加到最後位置以後。
function quickSort1(arr) {
  // 交換
  function swap(arr, a, b) {
    [arr[a],arr[b]] = [arr[b],arr[a]]
  }

  // 分區
  function partition(arr, left, right) {
    /** * 開始時不知最終pivot的存放位置,能夠先將pivot交換到後面去 * 這裏直接定義最右邊的元素爲基準 */
    const pivot = arr[right];
    /** * 存放小於pivot的元素時,是緊挨着上一元素的,不然空隙裏存放的多是大於pivot的元素, * 故聲明一個storeIndex變量,並初始化爲left來依次緊挨着存放小於pivot的元素。 */
    let storeIndex = left;
    for (let i = left; i < right; i++) {
      if (arr[i] < pivot) {
        /** * 遍歷數組,找到小於的pivot的元素,(大於pivot的元素會跳過) * 將循環i次時獲得的元素,經過swap交換放到storeIndex處, * 並對storeIndex遞增1,表示下一個可能要交換的位置 */
        swap(arr, storeIndex, i);
        storeIndex++;
      }
    }
    // 最後: 將pivot交換到storeIndex處,基準元素放置到最終正確位置上
    swap(arr, right, storeIndex);
    return storeIndex;
  }

  function sort(arr, left, right) {
    if (left > right) return;

    const storeIndex = partition(arr, left, right);
    sort(arr, left, storeIndex - 1);
    sort(arr, storeIndex + 1, right);
  }

  sort(arr, 0, arr.length - 1);
  return arr;
}
複製代碼
相關文章
相關標籤/搜索