算法入門及其C++實現


 https://github.com/yuwei67/Play-with-Algorithms

 

 

 

 

 

 

 

(nlogn)為最優排序算法

 

選擇排序

整個數組中,先選出最小元素的位置,將該位置與當前的第一位交換;然后選出剩下數組中,最小元素的位置,將此元素與第二位元素交換;以此類推

 

srand和rand函數使用前,需要包含 stdlib.h和time.h;

 

 插入排序

類似於玩撲克牌時的思想,看后面牌中的每一張牌,然后插入到前方合適的位置

單獨看8,不需要排序

6與前面的8比,6小於8,於是6和8互換位置

2先和8比,2小於8,於是互換位置

接下來2和6比,2小於6,於是互換位置,至此前三個數排序結束,后面的排序同理。

 

相較於選擇排序,不需要每次遍歷所有內容,有提前終止機會,當數組近乎有序時,插入排序效率遠高於選擇排序,甚至比很多O(nlogn)級別排序算法效率高;

優化插入排序:不貿然交換位置,思路如下

 

 

 

當考察 “2” 時,先把2這個元素復制一個副本,看是否應該放在這個位置,發現2比前面的8小,所以不應該放在這兒,那么將此位置的值賦值為8,然后考察2是不是應該放在原來8的位置;2比這個位置的前一個位置的6要小,所以6放到這個位置;之后看2是不是應該放在原來6的位置,由於是第0個位置,所以2應該放在此處,至此,對2的排序結束。

一次swap是3次賦值,優化后變為多次比較后1次賦值

插入排序,對近乎有序的數組,可以降到O(N)的復雜度

 

歸並排序(自頂向下,使用遞歸)

將數組對半分成2份,左右分別單獨排序,本質是遞歸排序的過程。

時間復雜度比O(N^2)小,但是需要開辟輔助空間

1比2小,所以1放在藍色指針當前位置,藍色指針后移,輔助空間中1對應的紅色指針后移

之后比較2和4,具體過程同前一步,以此類推。

具體實現:i 和 j 表示當前正在考慮的元素,k 指向這兩個元素相比較之后,最終應該放到歸並數組中的位置(是下一個需要放置的位置,不是已經排好序的最后一位)

代碼示例(未優化,對近乎有序數組性能較差):

 1 // 將arr[l...mid]和arr[mid+1...r]兩部分進行歸並
 2 template<typename  T>
 3 void __merge(T arr[], int l, int mid, int r){
 4 
 5     // 經測試,傳遞aux數組的性能效果並不好
 6     T aux[r-l+1];
 7     for( int i = l ; i <= r; i ++ )
 8         aux[i-l] = arr[i];
 9 
10     int i = l, j = mid+1;
11     for( int k = l ; k <= r; k ++ ){
12 
13         if( i > mid )   { arr[k] = aux[j-l]; j ++;}
14         else if( j > r ){ arr[k] = aux[i-l]; i ++;}
15         else if( aux[i-l] < aux[j-l] ){ arr[k] = aux[i-l]; i ++;}
16         else                          { arr[k] = aux[j-l]; j ++;}
17     }
18 }
19 
20 // 遞歸使用歸並排序,對arr[l...r]的范圍進行排序
21 template<typename T>
22 void __mergeSort(T arr[], int l, int r){
23 
24     if( l >= r )
25         return;
26 
27     int mid = (l+r)/2;
28     __mergeSort(arr, l, mid);
29     __mergeSort(arr, mid+1, r);
30     __merge(arr, l, mid, r);
31 }
32 
33 template<typename T>
34 void mergeSort(T arr[], int n){
35 
36     __mergeSort( arr , 0 , n-1 );
37 }

 代碼優化(第一次)

    

20 // 遞歸使用歸並排序,對arr[l...r]的范圍進行排序
21 template<typename T>
22 void __mergeSort(T arr[], int l, int r){
23 
    //對所有條件的優化(小數組使用插入排序,一是因為此時數組近乎有序的概率會比較大,
    //二是因為 N^2 和 NlogN 前是有常數的系數的,對於這個系數,插入排序比歸並排序小,所以當N小到一定程度時,插入排序會比歸並排序快一些)
    //15這個數是最優的么?
24 //if( l >= r ) 25 // return;
    if( r - l <= 15)
    {
      insertionSort(arr,l,r);
    } 26 27 int mid = (l+r)/2; 28 __mergeSort(arr, l, mid);
29 __mergeSort(arr, mid+1, r);
    //對近乎有序數組的優化
    if(arr[mid] > arr[mid+1]) 30   __merge(arr, l, mid, r); 31 }

歸並排序(自底向上,使用迭代,統計意義上效率稍弱於遞歸方式實現)

由於沒有使用索引直接獲取元素,可以非常好的使用NlogN的時間對鏈表這樣的數據結構進行排序

 

 

 

快速排序

思路:每次從當前數組中選擇一個元素,將這個元素想辦法挪到排好序的數組中應該在的位置,那么以這個元素為基點,前面的數比他小,后面的數比他大。

    之后對前后兩個數組分別繼續使用快速排序。

子過程如下:如果當前訪問的元素 e 比 v 大,i 指針直接后移;否則調換 i 和 j 后面元素的位置,j 指針后移, i 指針后移;遍歷完成后,交換 l 和 j 元素的位置。

 1 // 對arr[l...r]部分進行partition操作
 2 // 返回p,使得arr[l...p-1] < arr[p] ; arr[p+1...r] > arr[p]
 3 template <typename T>
 4 int __partition(T arr[], int l, int r){
 5 
 6     T v = arr[l];
 7 
 8     int j = l; // arr[l+1...j] < v ; arr[j+1...i) > v
 9     for( int i = l + 1 ; i <= r ; i ++ )
10         if( arr[i] < v ){
11             j ++;
12             swap( arr[j] , arr[i] );
13         }
14 
15     swap( arr[l] , arr[j]);
16 
17     return j;
18 }
19 
20 // 對arr[l...r]部分進行快速排序
21 template <typename T>
22 void __quickSort(T arr[], int l, int r){
23 
24     if( l >= r )
25         return;
26 
27     int p = __partition(arr, l, r);
28     __quickSort(arr, l, p-1 );
29     __quickSort(arr, p+1, r);
30 }
31 
32 template <typename T>
33 void quickSort(T arr[], int n){
34 
35     __quickSort(arr, 0, n-1);
36 }

快速排序優化(針對近乎有序數組)

 1 template <typename T>
 2 int _partition(T arr[], int l, int r){
 3 
 4     swap( arr[l] , arr[rand()%(r-l+1)+l] );  5 
 6     T v = arr[l];
 7     int j = l;
 8     for( int i = l + 1 ; i <= r ; i ++ )
 9         if( arr[i] < v ){
10             j ++;
11             swap( arr[j] , arr[i] );
12         }
13 
14     swap( arr[l] , arr[j]);
15 
16     return j;
17 }
18 
19 template <typename T>
20 void _quickSort(T arr[], int l, int r){
21 
22 //    if( l >= r )
23 //        return;
24     if( r - l <= 15 ){
25         insertionSort(arr,l,r);
26         return;
27     }
28 
29     int p = _partition(arr, l, r);
30     _quickSort(arr, l, p-1 );
31     _quickSort(arr, p+1, r);
32 }
33 
34 template <typename T>
35 void quickSort(T arr[], int n){
36 
37  srand(time(NULL)); 38     _quickSort(arr, 0, n-1);
39 }

 快速排序優化(雙路快速排序法,針對大量重復元素的數組)

之前小於 v 和大於 v 的數組放於數組的一段,優化后放於數組的兩端   

與之前的partition相比較,此方式的最大特點是將等於 v 的元素分散到兩側,不會出現大量相同元素集中在一側的情況

 1 template <typename T>
 2 int _partition2(T arr[], int l, int r){
 3 
 4     swap( arr[l] , arr[rand()%(r-l+1)+l] );
 5     T v = arr[l];
 6 
 7     // arr[l+1...i) <= v; arr(j...r] >= v
 8     int i = l+1, j = r;
 9     while( true ){ 10         while( i <= r && arr[i] < v ) 11             i ++; 12 
13         while( j >= l+1 && arr[j] > v ) 14             j --; 15 
16         if( i > j ) 17             break; 18 
19  swap( arr[i] , arr[j] ); 20         i ++; 21         j --; 22  } 23 
24  swap( arr[l] , arr[j]); 25 
26     return j;
27 }
28 
29 template <typename T>
30 void _quickSort(T arr[], int l, int r){
31 
32 //    if( l >= r )
33 //        return;
34     if( r - l <= 15 ){
35         insertionSort(arr,l,r);
36         return;
37     }
38 
39     int p = _partition2(arr, l, r);
40     _quickSort(arr, l, p-1 );
41     _quickSort(arr, p+1, r);
42 }

 

三路快速排序

 1 template <typename T>
 2 void __quickSort3Ways(T arr[], int l, int r){
 3 
 4     if( r - l <= 15 ){
 5         insertionSort(arr,l,r);
 6         return;
 7     }
 8 
 9     swap( arr[l], arr[rand()%(r-l+1)+l ] );
10 
11     T v = arr[l];
12 
13     int lt = l;     // arr[l+1...lt] < v
14     int gt = r + 1; // arr[gt...r] > v
15     int i = l+1;    // arr[lt+1...i) == v
16     while( i < gt ){
17         if( arr[i] < v ){
18             swap( arr[i], arr[lt+1]);
19             i ++;
20             lt ++;
21         }
22         else if( arr[i] > v ){
23             swap( arr[i], arr[gt-1]);
24             gt --;
25         }
26         else{ // arr[i] == v
27             i ++;
28         }
29     }
30 
31     swap( arr[l] , arr[lt] );
32 
33     __quickSort3Ways(arr, l, lt-1);
34     __quickSort3Ways(arr, gt, r);
35 }
36 
37 template <typename T>
38 void quickSort3Ways(T arr[], int n){
39 
40     srand(time(NULL));
41     __quickSort3Ways( arr, 0, n-1);
42 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM