O(n^2)以及O(nlogn)時間複雜度的排序算法

O(n^2)的算法

都是作的升序。算法

簡單選擇排序

思路:每次選擇還未排序的區間的最小值和未排序區間的第一個值交換。數組

 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 }

插入排序(insertion sort)

思路:當前位置的值與前面排好序的區間從後往前對比,找到適合的插入位置並插入。函數

適用於:近乎有序的排序,在幾乎有序的狀況下,它的速度會比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 }

冒泡排序

O(nlogn)的算法

歸併排序

優化:在待排區間的長度小於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 }

擴展

Merge Sort的思路求逆序對的個數

mergeSort第21行

Qiuck Sort求數組中第n大的數
 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 }
相關文章
相關標籤/搜索