前天看到知乎上有一篇文章在吐槽阮一峯老師的快速排序算法,這裏插一句題外話,我以爲人非聖賢孰能無過,盡信書不如無書,學習的過程也就是不斷髮現錯誤改正錯誤的過程,有人幫咱們糾正了這個錯誤咱們應該開心,可是我以爲不該該批判阮一峯老師,他也在不斷地學習,不斷地糾錯成長,因此你們都同樣,無所謂誤導,若是出錯的不是他,是更厲害的牛人呢?JavaScript的做者呢?因此你們都會出錯,咱們也應該多思考,抱着懷疑的態度接納,時刻思考這是否是最優的解法,還有沒有更好的呢,我想這纔是咱們應該作的.
而我,做爲一個計算機專業的前端,卻不能很好地實現各類思想的排序算法,我以爲很慚愧,因此我就抽時間仔細查看了<<數據結構與算法分析:C語言描述+中文版.pdf>>這本書,下面我就對我理解的各類思想的排序算法作一下總結,但願能夠給你們一些參考和收穫,若有不妥之處,煩請指出,也能夠分享大家以爲更好地想法,我以爲你們一塊兒學習一塊兒進步是最快樂的事~前端
(1) 時間複雜度的概念
算法的時間複雜度是一個函數,他定性地描述了某個算法的運行時間,經常使用大O符號,不包括這個函數的低階項和高階項係數.
(2) 計算方法算法
舉例以下:shell
for(i = 1; i<= n; i++) { for(j = 1; j <= n; j++) { c[i][j] = 0; // 該步驟屬於基本操做的執行次數: n的平方 for( k= 1;k <= n; k++) { c[i][j] += a[i][k] * b[k][j]; // 該步驟屬於基本操做的執行次數: n的三次方 } } }
咱們能夠獲得T(n) = n^3 + n^2,咱們能夠肯定n^3爲T(n)的同數量級,f(n)=n^3;而後T(n) / f(n) = 1 + 1/n 求極限爲常數1,因此該算法的時間複雜度爲:
T(n) = O(n^3);數組
說明: 爲了方便我接下來都是使用N來代指數組元素個數的.
個人建議: 我建議你們先看代碼,看不懂代碼的時候對着代碼看圖解,這樣方便更好的理解前端工程師
冒泡排序的主要思想就是對一個長度爲n的數組進行遍歷, i從n-1到1的,數組的前i個元素的最大值放在i位置上,假想冒泡排序是一個豎着的水柱,遍歷的過程就是,大的值(重的)不斷沉下來,小的值(輕的)不斷浮上去,這樣遍歷結束後,每一個位置上的值都比他前面的值大,排序結束.數據結構
最壞狀況下的時間複雜度: o(n^2);
最好狀況下的時間複雜度: o(n^2);dom
function bubbleSort(arr) { for(var i = arr.length - 1; i > 1; i--) { for(var j=0; j < i; j++) { if(arr[j] > arr[j+1]) { var temp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = temp; } } } return arr; } var arr = [34,8,64,51,32,21]; bubbleSort(arr); // [8, 21, 32, 34, 51, 64]
function bubbleSort(arr, n) { if(n <= 1) { return arr; } else { for(var j=0; j < n - 1; j++) { if(arr[j] > arr[j+1]) { var temp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = temp; } } return bubbleSort(arr, --n); } } var arr = [34,8,64,51,32,21]; bubbleSort(arr, arr.length); // [8, 21, 32, 34, 51, 64]
選擇排序的主要思想就是i 從 0 循環到到length - 1, 依次找出待排數組中從 i 到 length - 1 位置上的最小值放在 i 位置上.這樣最後獲得的數組就是排好序的數組了.函數
最壞狀況下的時間複雜度: o(n^2);
最好狀況下的時間複雜度: o(n^2);post
function selectSort(arr) { var len = arr.length, min = 0; for(var i = 0;i < len - 1; i++) { min = i; // 默認最小值的位置 for(var j = i + 1; j < len; j++){ if(arr[min] > arr[j]) { min = j; } } if(min != i) { var temp = arr[min];arr[min] = arr[i]; arr[i] = temp; } } return arr; } var arr = [34,8,64,51,32,21]; selectSort(arr);
function selectSort(arr, n, min) { var len = arr.length; if(n < len - 1) { for(var j = n + 1; j < len; j++){ if(arr[min] > arr[j]) { min = j; } } if(min != n) { var temp = arr[min];arr[min] = arr[n]; arr[n] = temp; } n++; return selectSort(arr, n, min); } return arr; } var arr = [34,8,64,51,32,21]; selectSort(arr, 0, 0);
插入排序有 n-1 趟排序組成,對於 i=1 到 i=n-1 趟,內層循環j從 i 到 1, 若是這其中有 j-1 位置上的元素大於 i 位置上的元素,就將該元素後移,知道條件不成立退出循環,這個時候大的值都被移動到後面了,j這個位置就是i位置上的元素應該在的位置.這樣保證了每次循環i位置前的全部元素都是排好序的,新的循環就只須要 將 i 位置上的元素 和 j-1(也就是初始的 i-1) 位置上的元素做比較,若是大於則無需再往前比較,若是小於則繼續往前比較後移.學習
最壞狀況下的時間複雜度: o(n^2);
最好狀況下的時間複雜度: o(n);
function insertSort(arr) { var n = arr.length,temp = 0; for(var i = 1; i < n; i++) { temp = arr[i]; for(j = i; j > 0 && arr[j-1] > temp; j--) { arr[j] = arr[j - 1]; } arr[j] = temp; } return arr; } var arr = [34,8,64,51,32,21]; insertSort(arr); // [8, 21, 32, 34, 51, 64]
function insertSort(arr, n) { if(n > 0 && n < arr.length){ var i = j = n, temp = arr[n]; while(j > 0 && arr[j - 1] > temp) { arr[j] = arr[j - 1]; j--; } arr[j] = temp; i++; return insertSort(arr, i); } return arr; } var arr = [34,8,64,51,32,21]; insertSort(arr, 1); // [8, 21, 32, 34, 51, 64]; // 這個函數的調用限定了第一次調用n的值只能傳1
顧名思義,快速排序是在實踐中最快的已知排序算法,它的平均運行時間是O(Nlog₂N).快速排序的關鍵在於樞紐元的選取,有一種比較推薦的選取方法就是選取左端的值,右端的值,中間位置的值(L(left + right) / 2)這三個數的中位數.舉例: 輸入爲8,1,4,9,6,3,5,2,7,0, 左邊元素8, 右邊元素0,中間位置上的元素L(0+9)/2是4位置上的元素是6,L在表示向下取整.
8,0,6的中位數,先排序0,6,8, 這三個數的中位數是6.
經過一趟排序將要排序的部分分割成獨立的兩部分,其中一部分數據都比另一部分的全部數據都要小,而後再按此方法對這兩部分數據進行快速排序,整個排序過程能夠遞歸進行,依次達到整個數據變成有序序列.
實現步驟
平均狀況下的時間複雜度: o(nlog₂n);
最好狀況下的時間複雜度: o(n);
function quickSort(arr, left, right) { if(left >= right) return; var i = left; var j = right - 1; var privot = arr[left]; //console.log(privot); while(i < j) { while(i<j && arr[j] >= privot) j--; arr[i] = arr[j]; while(i<j && arr[i] <= privot) i++; arr[j]=arr[i]; } arr[i]=privot; quickSort(arr, left, i); quickSort(arr, i+1, right); } var arr = [49,38,65,97,76,13,27,49,55,04]; quickSort(arr, 0, arr.length);
function mainProduce(arr, left, right) { var i = left, j = right - 1; var rendomIndex = Math.floor(Math.random() * (j - i)) + left; var temp = arr[left];arr[left] = arr[rendomIndex];arr[rendomIndex] = temp; var privot = arr[left]; while(i < j) { while(i<j && arr[j] >= privot) j--; var temp = arr[i];arr[i] = arr[j];arr[j] = temp; while(i<j && arr[i] <= privot) i++; var temp = arr[j];arr[j] = arr[i];arr[i] = temp; } arr[i]=privot; return i; } function quickSort(arr, left, right) { var s = []; if(left < right) { var mid = mainProduce(arr, left, right); if(mid > left + 1) { s.push(left);s.push(mid); } if(mid < right - 1) { s.push(mid + 1);s.push(right); } while(s.length !== 0) { right = s.pop(); left = s.pop(); mid = mainProduce(arr, left, right); if(mid > left + 1) { s.push(left);s.push(mid); } if(mid < right - 1) { s.push(mid + 1);s.push(right); } } } return arr; } var arr = [49,38,65,97,76,13,27,49,55,04]; quickSort(arr, 0, arr.length);
希爾排序是把記錄按照下標的必定增量分組,對每組使用插入排序;隨着增量逐漸減小,分割的數組愈來愈大,當增量減至1,整個數組排序完成,算法終止.
主要步驟
希爾排序是不穩定的,它在不斷地交換的過程當中會改變原來相等的元素的順序.
平均狀況下的時間複雜度: o(nlog₂n);
最好狀況下的時間複雜度: o(n);
圖片源於自百度百科: 圖片來源
function shellSort(arr, increment) { var len = arr.length; if(increment > 0) { for(var i = increment; i < len; i++) { for(var j = i - increment; j >= 0 && arr[j] > arr[j + increment]; j -= increment) { var temp = arr[j]; arr[j] = arr[j + increment]; arr[j + increment] = temp; } } return shellSort(arr, Math.floor(increment/2)); } return arr; } var arr = [49,38,65,97,76,13,27,49,55,04]; shellSort(arr, Math.floor(arr.length / 2));
function shellSort(arr) { var len = arr.length; for(var increment = Math.floor(len / 2); increment > 0; increment = Math.floor(increment / 2)) { for(var i = increment; i < len; i++) { for(var j = i - increment; j >= 0 && arr[j] > arr[j + increment]; j -= increment) { var temp = arr[j]; arr[j] = arr[j + increment]; arr[j + increment] = temp; } } } return arr; } var arr = [49,38,65,97,76,13,27,49,55,04]; shellSort(arr);
希爾排序的主要思想就是遞歸將數組層層分割,直到分割成最小的單元,而後再比較,提供一個新的空數組arrayC,將分割的左右兩個數組中小的數放進數組,而後再層層回溯向上合併.獲得最終的arrayC就是排序後的數組.
平均狀況下的時間複雜度: O(nlog₂n);
最好狀況下的時間複雜度: O(nlog₂n) ;
var result = []; function mergeArray(left, right) { result = []; while(left.length > 0 && right.length > 0) { if(left[0] < right[0]) { result.push(left.shift()); } else { result.push(right.shift()); } } return result.concat(left).concat(right); } function mergerSort(arr) { if(arr.length <= 1) { return arr; } var middle = Math.floor(arr.length / 2); var left = arr.slice(0, middle); var right = arr.slice(middle); return mergeArray(mergerSort(left), mergerSort(right)); } var arr = [49,38,65,97,76,13,27,49,55,04]; mergerSort(arr, 0, arr.length);
因爲歸併排序的非遞歸實現比較複雜,我這裏就不作講解了,我以爲若是真的須要用到,讀者可自行研究.
這是我寫的最用心的一篇博客了,萬事開頭難,我已經開頭了,就是一種突破.但願我能夠繼續堅持下去,不斷充電,不斷輸出,成爲一個優秀的前端工程師,加油 ^-^ ^-^.
歡迎幫我糾正錯誤和有疑問的人與我交流, it will be my pleasure. 個人qq號: 2510909248.
推薦閱讀
1) 十大經典排序算法總結(JavaScript描述)