算法入门及其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