都是作的升序。算法
思路:每次選擇還未排序的區間的最小值和未排序區間的第一個值交換。數組
1 function selectSort(arr){ 2 for(let i = 0; i < arr.length; i++){ 3 let minIdx = i; 4 for(let j = i; j < arr.length; j++){ 5 if(arr[j] < arr[minIdx]){ 6 minIdx = j; 7 } 8 } 9 let tmp = arr[i]; 10 arr[i] = arr[minIdx]; 11 arr[minIdx] = tmp; 12 } 13 return arr; 14 }
思路:當前位置的值與前面排好序的區間從後往前對比,找到適合的插入位置並插入。函數
適用於:近乎有序的排序,在幾乎有序的狀況下,它的速度會比n(logn)的算法還要快,能夠接近O(n),插入排序要優於選擇排序優化
1 function insertSort(arr){ 2 let tmp; 3 for(let i = 0; i < arr.length; i++){ 4 for(let j = i - 1; j >= 0; j--){ 5 if(j === 0){ 6 if(arr[i] < arr[j]){ 7 // 在順序表中,應該使用副本複製當前的值,採用賦值去修改數組,而不是刪除和插入,由於在順序表中刪除和插入的時間複雜度是n 8 tmp = arr.splice(i,1)[0]; 9 arr.splice(j,0,tmp); 10 } 11 }else if(arr[i] < arr[j] && arr[i] >= arr[j - 1]){ 12 tmp = arr.splice(i,1)[0]; 13 arr.splice(j,0,tmp); 14 } 15 } 16 // 作一個當前函數是否有序的判斷 17 if(isSort(arr)){ 18 return arr; 19 } 20 } 21 return arr; 22 }
優化:在待排區間的長度小於100時,能夠用插入排序ui
1 // 自下而上的歸併算法,自上而下須要用到遞歸 2 function mergeSort(arr){ 3 let arrSort = [],n = arr.length,count = 0; 4 for(let size = 1; size <= n; size += size){ // size表示當前有序區間的長度 5 for(let i = 0; i < n; i += size*2){ 6 // 將[i...i+size-1]和[i+size...i+size*2-1]的兩個區間融合成一個有序的並添加到arrSort後面 7 arrSort = arrSort.concat(orderMerge(arr.slice(i,i+size),arr.slice(i+size,((i+size*2) > n? n : (i+size*2))))); 8 } 9 arr = arrSort.slice(0); 10 arrSort.length = 0; 11 } 12 return {arr,count}; 13 // orderMerger 函數主要是講有序的兩個表融合成一個有序表 14 function orderMerge(arr1,arr2){ 15 let arr = []; 16 let idx1 = 0, idx2 = 0; 17 while(idx1 < arr1.length && idx2 < arr2.length){ 18 if(arr1[idx1] <= arr2[idx2]){ 19 arr.push(arr1[idx1++]); 20 }else{ 21 // 當arr1[index1] > arr2[idx2]的時候就是一個逆序對 22 arr.push(arr2[idx2++]); 23 count++; 24 } 25 } 26 if(idx1 === arr1.length){ 27 arr = arr.concat(arr2.slice(idx2)); 28 }else{ 29 arr = arr.concat(arr1.slice(idx1)); 30 } 31 return arr; 32 } 33 }
思想:選擇待排序的區間中第一個p數做爲參照,大於p的放在p的後面,小於p的放前面。最後將p放在兩個區間的中間,遞歸下去。spa
缺點:
: 1 在排近乎有序的數組的時候,時間複雜度趨近於O(n^2)code
2 再相同數值不少的數組中,時間複雜度趨近於O(n^2)blog
解決方案
: 優化缺點1:隨機選擇參照數p或者待排數組中間的數做爲參照數排序
優化缺點2-1 :從待排數組兩邊進行遍歷,從左向右遇到>=p的值停頓,在從右向左遇到<=p的值停頓,將兩個數交換並i++,j--,知道i == j爲止遞歸
優化缺點2-2:三路快速排序,有三個數組,一個數組存放小於p的值,一個存放大於p的值,一個存放等於p的值。
1 function quickSort(arr){ 2 // 終止條件:當數組長度小於等於1是返回該數組 3 if(arr.length <= 1){ 4 return arr; 5 } 6 // 定義leftSort、rightSort存放小於和大於p的數 7 let leftSort = [], rightSort =[], midSort = [], odd = 0; 8 // 遍歷arr,將大於p的放在rightSort中,小於p的放在leftSort中 9 //優化缺點1:選擇中間的數作參照並把該數從原數組中刪除 10 let idx = Math.floor(arr.length/2); 11 let p = arr.splice(idx,1)[0]; 12 for(let i = 0; i < arr.length; i++){ 13 if(arr[i] > p){ 14 rightSort.push(arr[i]); 15 }else if(arr[i] < p){ 16 leftSort.push(arr[i]); 17 }else{ 18 // 優化缺點2 19 if(odd){ 20 leftSort.push(arr[i]); 21 odd = 1; 22 }else{ 23 rightSort.push(arr[i]); 24 odd = 0; 25 } 26 } 27 } 28 // 遞歸leftSort、rightSort 29 leftSort = quickSort(leftSort); 30 rightSort = quickSort(rightSort); 31 // 將leftSort、midSort、rightSort合併成一個數組並返回。 32 arr = leftSort.concat(p,rightSort) 33 return arr; 34 } 35 36 //優化缺點2-2 37 function quickSort(arr){ 38 if(arr.length <= 1){ 39 return arr; 40 } 41 let leftSort = [], rightSort =[], midSort = []; 42 let idx = Math.floor(arr.length/2); 43 let p = arr[idx]; // 能夠不用刪除了 44 for(let i = 0; i < arr.length; i++){ 45 if(arr[i] > p){ 46 rightSort.push(arr[i]); 47 }else if(arr[i] < p){ 48 leftSort.push(arr[i]); 49 }else{ 50 midSort.push(arr[i]); 51 } 52 } 53 arr = quickSort(leftSort).concat(midSort,quickSort(rightSort)) 54 return arr; 55 }
mergeSort第21行
1 function theNumberN(arr,n){ 2 3 let p,idx, 4 leftSort = [], 5 midSort = [], 6 rightSort = []; 7 while(arr.length > 1){ 8 idx = Math.floor(arr.length/2); 9 p = arr[idx]; 10 for(let i = 0; i < arr.length; i++){ 11 if(arr[i] < p){ 12 leftSort.push(arr[i]); 13 }else if(arr[i] > p){ 14 rightSort.push(arr[i]); 15 }else{ 16 midSort.push(arr[i]); 17 } 18 } 19 if(leftSort.length >= n){ 20 arr = leftSort.slice(0); 21 } 22 23 else if(leftSort.length+midSort.length >= n){ 24 25 return midSort[0]; 26 } 27 28 else{ 29 30 arr = rightSort.slice(0); 31 n = n - leftSort.length - midSort.length; 32 } 33 leftSort.length = midSort.length = rightSort.length = 0; 34 35 } 36 return arr[0]; 37 38 }
或者使用遞歸
1 function theNumberN(arr,n){ 2 if(arr.length <= 1){ 3 return arr[0]; 4 } 5 // 選一個基點p 6 let p, 7 leftSort = [], 8 midSort = [], 9 rightSort = []; 10 // 小於p放leftSort,等於p放midSort,大於放rightSort 11 p = arr[Math.floor(arr.length/2)]; 12 for(let i = 0; i < arr.length; i++){ 13 if(arr[i] < p){ 14 leftSort.push(arr[i]); 15 }else if(arr[i] > p){ 16 rightSort.push(arr[i]); 17 }else{ 18 midSort.push(arr[i]); 19 } 20 } 21 // 若是leftSort.length>n則拋棄midSort和rightSort再找leftSort中第n大的數 22 if(leftSort.length >= n){ 23 return theNumberN(leftSort,n); 24 } 25 // 不然判斷leftSort.length+midSort.length>n則拋棄leftSort和rightSort,在midSort中找第n-leftSort.length大的數 26 else if(leftSort.length+midSort.length >= n){ 27 // theNumberN(midSort,n - leftSort.length); 28 return midSort[0]; 29 } 30 // 不然拋棄leftSort和midSort,在rightSort中找第n-leftSort.length-midSort.length大的數 31 else{ 32 return theNumberN(rightSort,n - leftSort.length - midSort.length); 33 } 34 }