排序圖解:js排序算法實現


之前寫過js實現數組去重, 今天繼續研究數組: 排序算法實現。 排序是數據結構主要內容,並不限於語言主要在於思想;大學曾經用C語言研究過一段時間的排序實現, 這段時間有空用JS再將排序知識點熟悉一遍。

理解排序不得不提的是日本人實現的一個排序動畫站, 該站對於研究排序大有益處。當然本文的排序算法並不與其一致, 本文是9種js排序實現的實踐與完善: 理解其9種算法然后使每種算法代碼均能正常運行。

1.插入排序

最普通的排序算法, 從數組下標1開始每增1項排序一次,越往后遍歷次數越多;

原理圖:

代碼:

 

// 插入排序 從下標1開始每增1項排序一次,越往后遍歷次數越多
function sort1(array) {
  var len = array.length,
      i, j, tmp, result;
  
  // 設置數組副本
  result = array.slice(0);
  for(i=1; i < len; i++){
    tmp = result[i];
    j = i - 1;
    while(j>=0 && tmp < result[j]){
      result[j+1] = result[j];
      j--;
    }
    result[j+1] = tmp;
  }
  return result;
}

 

2.二分插入排序

插入排序的一種優化實現, 通過二分法減少遍歷時間。

原理圖:

代碼:

// 先在有序區通過二分查找的方法找到移動元素的起始位置,
// 然后通過這個起始位置將后面所有的元素后移
function sort2(array) {
  var len = array.length,
      i, j, tmp, low, high, mid, result;
  // 賦予數組副本
  result = array.slice(0);
  for(i = 1; i < len; i++){
    tmp = result[i];
    low = 0;
    high = i - 1;
    while(low <= high){
      mid = parseInt((low + high)/2, 10);
      if(tmp < result[mid]) high = mid - 1;
      else low = mid + 1;
    }
    for(j = i - 1; j >= high+1; j--){
      result[j+1] = result[j];            
    }
    result[j+1] = tmp;
  }
  return result;
}

3.希爾排序

其排序思路有點復雜, 需花多點時間理解;排序思路:先將整個待排序記錄序列分割成若干個子序列,在序列內分別進行直接插入排序,待整個序列基本有序時,再對全體記錄進行一次直接插入排序。

原理圖:

代碼:

 

// 希爾排序:先將整個待排序記錄序列分割成若干個子序列
// 在序列內分別進行直接插入排序,待整個序列基本有序時,
// 再對全體記錄進行一次直接插入排序
function sort3(array){
  var len = array.length, gap = parseInt(len/2), 
      i, j, tmp, result;
  // 復制數組
  result = array.slice(0);
  while(gap > 0){
    for(i = gap; i < len; i++){
      tmp = result[i];
      j = i - gap;
      while(j>=0 && tmp < result[j]){
        result[j + gap] = result[j];
        j = j - gap;
      }
      result[j + gap] = tmp;
    }
    gap = parseInt(gap/2);
  }
  return result;
}

 

4.冒泡排序

很常見很容易理解的排序算法, 排序思路:遍歷數組,每次遍歷就將最大(或最小)值推至最前。越往后遍歷查詢次數越少, 跟插入排序剛好相反。

原理圖:

代碼:

// 冒泡排序 每次將最小元素推至最前
function sort4(array) {
  var len = array.length,
  i, j, tmp, result;
  result = array.slice(0);
  for (i = 0; i < len; i++) {
    for (j = len - 1; j > i; j--) {
      if (result[j] < result[j - 1]) {
        tmp = result[j - 1];
        result[j - 1] = result[j];
        result[j] = tmp;
      }
    }
  }
  return result;
}

 

5.改進冒泡排序

對上述冒泡排序的一種優化, 優化思路:當一次遍歷前后數組不產生變化時,說明該數組已經有序,結束排序。

原理圖:

代碼:

// 如果在某次的排序中沒有出現交換的情況,
// 那么說明在無序的元素現在已經是有序了,就可以直接返回了。
function sort5(array) {
  var len = array.length,
  i, j, tmp, exchange, result;

  result = array.slice(0);
  for (i = 0; i < len; i++) {
    exchange = 0;
    for (j = len - 1; j > i; j--) {
      if (result[j] < result[j - 1]) {
        tmp = result[j];
        result[j] = result[j - 1];
        result[j - 1] = tmp;
        exchange = 1;
      }
    }
    if (!exchange) return result;
  }
  return result;
}

 

6.快速排序

快速排序在諸多算法排序中可能不是最好的, 但個人認為在JS語言實現中是最快的!以前公司項目中對比過二分插入排序優化冒泡排序快速排序的JS實現執行時間,幾千條數據的數組在firefox下快速排序的速度比冒泡、插入排序快3至4秒(數組元素為復雜的對象,根據對象某一屬性值排序)。阮一峰老師研究JS實現排序時曾只針對該種排序進行講解:javascript的快速排序實現

原理圖:

代碼:

//(1)在數據集之中,選擇一個元素作為"基准"(pivot)。
//(2)所有小於"基准"的元素,都移到"基准"的左邊;所有大於"基准"的元素,都移到"基准"的右邊。
//(3)對"基准"左邊和右邊的兩個子集,不斷重復第一步和第二步,直到所有子集只剩下一個元素為止。
function sort6(array) {
  var tmp_array = array.slice(0), result,
  quickSort = function(arr) {
  if (arr.length <= 1) { return arr; }
  var pivotIndex = Math.floor(arr.length / 2);
  var pivot = arr.splice(pivotIndex, 1)[0];
  var left = [];
  var right = [];
  for (var i = 0; i < arr.length; i++){
    if (arr[i] < pivot) {
      left.push(arr[i]);
    } else {
      right.push(arr[i]);
    }
  }
  return quickSort(left).concat([pivot], quickSort(right));
  };
  result = quickSort(tmp_array);
  return result;
}

 

7.選擇排序

實現思路跟冒泡排序差不多, 可以說是冒泡排序的衍生版本;

原理圖:

代碼:

// 在無序區中選出最小的元素,然后將它和無序區的第一個元素交換位置。
// 原理跟冒泡排序一樣,算是冒泡的衍生版本
function sort7(array) {
  var len = array.length,
  i, j, k, tmp, result;

  result = array.slice(0);
  for (i = 0; i < len; i++) {
    k = i;
    for (j = i + 1; j < len; j++) {
      if (result[j] < result[k]) k = j;
    }
    if (k != i) {
      tmp = result[k];
      result[k] = result[i];
      result[i] = tmp;
    }
  }
  return result;
}

 

8.堆排序

因為js模擬二叉樹比較麻煩,所以堆排序的優勢用js語言無法體現, 相對而言C語言的鏈表在實現上更能表現堆排序,堆排序或許更適合指針類的計算機語言。本文注重圖解各排序的基本思路,所以該排序的具體實現沒講太細, 如想深究實現細節請看:堆排序及其分析

原理圖:

1.調整二叉樹,形成大根堆(子節點都比父節點小)。

2.交換堆第一元素跟最后元素位置,最后元素彈出堆。然后繼續回到1,調整堆。

 

3.重復2, 當所有節點彈出堆后;彈出的節點值就是有序的了。

代碼:

// 1) 初始堆:將原始數組調整成大根堆的方法——篩選算法:子節點都比父節點小
// 2) 堆排序: 每次將堆頂元素與數組最后面的且沒有被置換的元素互換。
// 參考代碼: http://bubkoo.com/2014/01/14/sort-algorithm/heap-sort/
function sort8(array) {
  var result = array.slice(0);

  function swap(array, i, j) {
    var temp = array[i];
    array[i] = array[j];
    array[j] = temp;
  }

  function maxHeapify(array, index, heapSize) {
    var iMax, iLeft, iRight;
    while (true) {
      iMax = index;
      iLeft = 2 * index + 1;
      iRight = 2 * (index + 1);

      if (iLeft < heapSize && array[index] < array[iLeft]) {
        iMax = iLeft;
      }

      if (iRight < heapSize && array[iMax] < array[iRight]) {
        iMax = iRight;
      }

      if (iMax != index) {
        swap(array, iMax, index);
        index = iMax;
      } else {
        break;
      }
    }
  }

  function buildMaxHeap(array) {
    var i, iParent = Math.floor(array.length / 2) - 1;

    for (i = iParent; i >= 0; i--) {
      maxHeapify(array, i, array.length);
    }
  }

  function sort(array) {
    buildMaxHeap(array);

    for (var i = array.length - 1; i > 0; i--) {
      swap(array, 0, i);
      maxHeapify(array, 0, i);
    }
    return array;
  }

  return sort(result);
}

 

9.歸並排序

很容易理解且執行效率一般(js實現)的排序, 排序思路:將無序的數組 拆成N部分進行有序處理,然后合並;

原理圖:

代碼:

// 合並排序:將無序的數組 拆成N部分進行有序處理,然后合並;
// 參考代碼: https://gist.github.com/paullewis/1982121
function sort9(array) {
  var result = array.slice(0);

  // 遞歸調用合並函數
  function sort(array) {
    var length = array.length,
    mid = Math.floor(length * 0.5),
    left = array.slice(0, mid),
    right = array.slice(mid, length);

    if (length === 1) {
      return array;
    }
    return merge(sort(left), sort(right));
  }

  // 合並 兩有序的數組
  function merge(left, right) {
    var result = [];

    while (left.length || right.length) {

      if (left.length && right.length) {

        if (left[0] < right[0]) {
          result.push(left.shift());
        } else {
          result.push(right.shift());
        }

      } else if (left.length) {
        result.push(left.shift());
      } else {
        result.push(right.shift());
      }
    }
    return result;
  }

  return sort(result);
}

 

本文代碼及demo:

demo源碼

 

參考文章:

1.排序動畫站

2.9種js排序實現

3.阮一峰:javascript的快速排序實現

4.堆排序及其分析

5.常見排序算法

6.github: paullewis 歸並算法實現

 

 

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM