作為一個程序員,你怎么能不了解冒泡算法呢?
下面向大家介紹六中排序算法,並提供javascript實現,以及簡單分析算法復雜度。
1. 簡單排序方法
1.1 冒泡排序
總體描述:
相鄰元素進行比較,每次選取最大的元素,進行下一次比較,因此可以將最大的元素像冒泡一樣,從某一位置,到達最頂端
算法簡單描述:
假設:共有n個元素
進行(n-1)次循環,第i(從1開始計數)次循環獲得第i大的元素,放在數組第(n-i)(數組從0開始計數)位
每次循環都從第一個元素開始,比較當前元素與其后一個元素的大小關系,如果后一個元素小於當前元素,則說明,當前元素較大,互換位置,即將(0~n-i區間最大的數放在n-i位),並將當前元素指向下一個位置,直到當前位置指向0,循環結束
完整代碼:
function bubbleSort(originArr) { /* 數組副本 */ var cloneArr = originArr.concat(); /* 用於交換數據 */ var temp; /* 數組長度 */ var len = cloneArr.length; /* 每次選擇出最大的元素 */ /* 通過比較相鄰元素,將較大的元素放在后面,將較大的數繼續進行比較 */ for (var i = 0; i < len - 1; ++i) { // 需要執行len-1次 for (var j = 0; j < len - i; ++j) { // 需要執行len-i-1次 // 如果當前元素大於下一個元素,互換兩個元素 if (cloneArr[j] > cloneArr[j + 1]) { // 執行(1 + 2 + ... + n-1)算法復雜度為O(n^2) temp = cloneArr[j]; cloneArr[j] = cloneArr[j + 1]; cloneArr[j + 1] = temp; } } } return cloneArr; }
1.2 選擇排序
總體描述:
每次選擇最小的元素,放在相應的位置上
算法簡單描述:
假設:共有n個元素
進行(n-1)次循環,第i(從1開始計數)次循環獲得第i小的元素,放在數組第(i-1)(數組從0開始計數)位,第i次循環,從數組第(i-1)位開始,將該位置元素與其后所有元素進行比較,獲取較小元素索引,循環結束之后,將當前元素與最小索引位置元素位置互換,當前位置向前移動,進行下一輪循環,直到當前位置指向(n-1)
完整代碼:
function selectionSort(originArr) { /* 數組副本 */ var cloneArr = originArr.concat(); /* 用於交換數據 */ var temp; /* 存放最小元素索引 */ var minIndex = 0; /* 數組長度 */ var len = cloneArr.length; /* 從第一個位置開始,比較當前位置和后面所有元素,獲取最小元素后面的位置 */ for (var i = 0; i < len - 1; ++i) { // 需要執行len-1次 minIndex = i; // 獲取最小元素位置 for (var j = i + 1; j < len; ++j) { // 需要執行len-i-1次 if (cloneArr[minIndex] > cloneArr[j]) { // 執行(1 + 2 + ... + n-1)算法復雜度為O(n^2) minIndex = j; } } // 如果最小元素所在索引,不是當前位置,交換元素 if (minIndex !== i) { temp = cloneArr[i]; cloneArr[i] = cloneArr[minIndex]; cloneArr[minIndex] = temp; } } return cloneArr; }
1.3 插入排序
總體描述:
將數組分為前后兩部分,前一部分是已排序的元素集合,后一部分是未排序的元素集合。每次選中未排序的第一個數組,插入到已排序集合中的合適的位置
算法簡單描述:
假設:共有n個元素
從第2個元素開始,進行(n-1)次循環,第i次循環,將第i個元素插入到之前位置(1~i-1)中,將當前元素依次后面元素進行比較。比較元素起始值為當前元素前一位置元素,如果當前元素小於比較元素,比較元素向數組后面移動,當前元素繼續與下一個元素進行比較,直到比較元素位置為0或者當前元素大於比較元素,將元素插入當前比較位置
完整代碼:
function insertSort(originArr) { /* 數組副本 */ var cloneArr = originArr.concat(); /* 用於交換數據 */ var temp; /* 數組長度 */ var len = cloneArr.length; /* 循環數組中的每一個元素 */ for (var i = 1; i < len; ++i) { // 需要執行len-1次 // 記錄要插入的值 temp = cloneArr[i]; // 找到合適的位置插入 for (var j = i - 1; j >= 0; --j) { // 需要執行i次 if (temp < cloneArr[j]) { // 執行(1 + 2 + ... + n-1)算法復雜度為O(n^2) // 右移已排序數組 cloneArr[j + 1] = cloneArr[j]; } else { break; } } cloneArr[j + 1] = temp; } return cloneArr; }
2. 高級排序算法
2.1 希爾排序
總體描述:
希爾排序就是插入排序的優化,插入排序,每次將當前元素與之前的每一個元素進行比較,然后插入,希爾排序,相當於先按照一定步長,將數組進行分組,對每一組進行插入排序,這樣就可以大幅度的調整數據的分布情況,最后執行一次快速排序進行微調
算法簡單描述:
對於間隔數組中的每個元素gap,將數組元素根據gap分為gap組,對於每組進行插入排序
完整代碼:
function shellSort(originArr) { /* 數組副本 */ var cloneArr = originArr.concat(); /* 用於交換數據 */ var temp; /* 數組長度 */ var len = cloneArr.length; /* 間隔數組 */ var gap = []; /* 動態創建間隔數組 */ for (var i = Math.floor(len / 2); i > 0;) { gap.push(i); i = Math.floor(i / 2); } /* 使用間隔數組中的每一個元素,選擇數組中的元素,進行快速排序 */ /* 方法1: 對每一個間隔的每一個分組進行快速排序 */ /*for (var i = 0, gapLen = gap.length; i < gapLen; ++i) { // 分為gap[i]組分別進行排序 for (var j = 0; j < gap[i]; ++j) { // 第j組進行排序 for (var k = j + gap[i]; k < len; k = k + gap[i]) { temp = cloneArr[k]; while (k - gap[i] >= 0 && cloneArr[k - gap[i]] > temp) { cloneArr[k] = cloneArr[k - gap[i]]; k = k - gap[i]; } cloneArr[k] = temp; } } }*/ /* 方法2: 對於每一個間隔,從間隔位置開始,對其后每一個元素進行快速排序,保證前面的已經排好序 */ for (var i = 0, gapLen = gap.length; i < gapLen; ++i) { for (var j = gap[i]; j < len; ++j) { temp = cloneArr[j]; var k = j; while (k - gap[i] >= 0 && cloneArr[k - gap[i]] > temp) { cloneArr[k] = cloneArr[k - gap[i]]; k -= gap[i]; } cloneArr[k] = temp; }
// 最壞情況O(n(logn)^2) 平均情況O(n(logn)^2) } return cloneArr; }
2.2 快速排序
總體描述:
每次選取一個基准值,將數組中其他的元素和它進行比較,大於則移到數組右邊,小於則移到左邊。然后分類出來的數組繼續進行上述操作。
算法簡單描述:
選擇數組第一位元素位基准值,創建兩個新數組,分別存放小於基准值和大於基准值的元素。然后這兩個新數組遞歸進行上述操作,直到數組為空。然后將左右數組和基准值進行拼接
完整代碼:
function quickSort(originArr) { /* 如果數組為空,直接返回 */ if (originArr.length === 0) { return []; } /* 基准值 */ var pivot = originArr[0]; /* 分別存放大於小於數組 */ var lesser = []; var greater = []; /* 小於基准值,存放到lesser數組中,否則存放到greater數組中 */ for (var i = 1; i < originArr.length; ++i) { if (originArr[i] < pivot) { lesser.push(originArr[i]); } else { greater.push(originArr[i]); }
// 最壞情況O(n^2) 平均情況O(nlogn) } /* 將數組拼接后返回 */ return quickSort(lesser).concat(pivot, quickSort(greater)); }
2.3 歸並排序
總體描述:
自頂向下:先通過遞歸分解數組,再合並數組
算法簡單描述:
分解數組:如果數組長度不為1,從中間將數組分為兩部分,繼續分解
合並數組:將分解的數組融合,創建一個新數組,用於存放融合的數組元素。創建指針分別指向兩個數組的首位,比較當前指針指向位置元素的大小,將較小的元素插入新數組中,指針向后移動,直到有一個數組元素全部移出。最后檢查兩個數組,將未移出的元素追加到新數組中,最后存放已排序的數組根據對應位置存入待排序數組中
完整代碼:
function mergeSort(originArr) { /* 數組副本 */ var cloneArr = originArr.concat(); /* 調用歸並排序 */ doMergeSort(cloneArr, 0, cloneArr.length - 1); return cloneArr; } /* 向下分解數組,遞歸調用 */ function doMergeSort(arr, low, high) { if (low < high) { var mid = low + Math.floor((high - low) / 2); doMergeSort(arr, low, mid); doMergeSort(arr, mid + 1, high); merge(arr, low, mid, high);
// 最壞情況O(nlogn) 平均情況O(nlogn) } } /* 數組融合 */ function merge(arr, low, mid, high) { var p_low = low; var p_high = mid + 1; var sortArr = []; /* 比較左右部分元素,將較小的元素存放在sortArr前面 */ while (p_low <= mid && p_high <= high) { if (arr[p_low] > arr[p_high]) { sortArr.push(arr[p_high++]); } else { sortArr.push(arr[p_low++]); } } /* 將兩部分可能剩余的元素復制到數組中 */ while (p_high <= high) { sortArr.push(arr[p_high++]); } while (p_low <= mid) { sortArr.push(arr[p_low++]); } /* 將已排序的數組復制到原數組對應位置 */ for (var i = low; i < high + 1; i++) { arr[i] = sortArr[i - low]; } }
3. 最終測試結果
100000數組測試: