之前寫過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:
參考文章:
1.排序動畫站;
2.9種js排序實現;
4.堆排序及其分析;
5.常見排序算法;