各种内排序算法的C++实现

来源:互联网 发布:高嘉瑜 知乎 编辑:程序博客网 时间:2024/04/27 04:56

各种内排序算法的C++实现

            摘自http://blog.csdn.net/piaojun_pj/article/details/5911914

 

 

和很多计算机系的同学们一样,我在大学二年级时也学了《数据结构》这门课。当时我的老师是一个中科大的博士,现在已经是教授了。他在课上曾经这样评价这门课:《数据结构》几乎是所有计算机课程的基础课,如果把这门课学好了,其他的专业课就不成问题了。还有,IT公司的面试经常涉及到数据结构的相关知识,该课程的重要性由此可见。但是当时年少无知根本没好好学习,等到笔试,面试时才幡然悔悟。下面的内排序算法可算是数据结构中的重要内容,程序代码全部用C++实现,已在visual C++6.0上运行过了。

 

一.插入排序(insert sorting)

最差情况下,直接插入排序的最大时间代价为θ(n²),最小时间代价为θ(n),平均时间代价为θ(n²)。

 

[cpp] view plaincopyprint?
  1. #include <iostream>  
  2. using namespace std;  
  3.   
  4. /*交换函数,作用是交换数组中的两个元素的位置*/  
  5. void swap(int array[],int i,int j)  
  6. {  
  7.     int tmp=array[i];  
  8.     array[i]=array[j];  
  9.     array[j]=tmp;  
  10. }  
  11.   
  12. /*插入排序*/  
  13. void InsertSort(int array[],int n)  
  14. {  
  15.     for(int i=1;i<n;i++)  
  16.     {  
  17.         for(int j=i;j>0;j--)  
  18.         {  
  19.             if(array[j]>array[j-1])  
  20.                 swap(array,j,j-1);  
  21.             else  
  22.                 break;  
  23.         }  
  24.     }  
  25. }  
  26.   
  27. int main()  
  28. {  
  29.     int array[5]={3,1,2,5,4};  
  30.     InsertSort(array,5);  
  31.     for(int i=0;i<5;i++)  
  32.         cout<<array[i]<<"  ";  
  33.     cout<<endl;  
  34.     return 0;  
  35. }  
   

 

二.冒泡排序(bubble sorting)

冒泡排序的最大时间代价,最小时间代价和平均时间代价均为θ(n²)。

 

[cpp] view plaincopyprint?
  1. #include <iostream>  
  2. using namespace std;  
  3.   
  4. /*交换函数,作用是交换数组中的两个元素的位置*/  
  5. void swap(int array[],int i,int j)  
  6. {  
  7.     int tmp=array[i];  
  8.     array[i]=array[j];  
  9.     array[j]=tmp;  
  10. }  
  11.   
  12. /*冒泡排序*/  
  13. void BubbleSort(int array[],int n)  
  14. {  
  15.     for(int i=0;i<n-1;i++)  
  16.     {  
  17.         for(int j=n-1;j>i;j--)  
  18.         {  
  19.             if(array[j]<array[j-1])  
  20.                 swap(array,j,j-1);  
  21.         }  
  22.     }  
  23. }  
  24.   
  25. int main()  
  26. {  
  27.     int array[5]={3,1,2,5,4};  
  28.     BubbleSort(array,5);  
  29.     for(int i=0;i<5;i++)  
  30.         cout<<array[i]<<"  ";  
  31.     cout<<endl;  
  32.     return 0;  
  33. }  
   

 

三.选择排序(selection sorting)

 选择排序的最大时间代价,最小时间代价和平均时间代价均为θ(n²)。选择排序不依赖于原始数组的输入顺序。

[cpp] view plaincopyprint?
  1. #include <iostream>  
  2. using namespace std;  
  3.   
  4. /*交换函数,作用是交换数组中的两个元素的位置*/  
  5. void swap(int array[],int i,int j)  
  6. {  
  7.     int tmp=array[i];  
  8.     array[i]=array[j];  
  9.     array[j]=tmp;  
  10. }  
  11.   
  12. /*选择排序*/  
  13. void SelectionSort(int array[],int n)  
  14. {  
  15.     for(int i=0;i<n-1;i++)  
  16.     {  
  17.         int smallest=i;  
  18.         for(int j=i+1;j<n;j++)  
  19.         {  
  20.             if(array[smallest]>array[j])  
  21.                 smallest=j;  
  22.         }  
  23.         swap(array,i,smallest);  
  24.     }  
  25. }  
  26.   
  27. int main()  
  28. {  
  29.     int array[5]={3,1,2,5,4};  
  30.     SelectionSort(array,5);  
  31.     for(int i=0;i<5;i++)  
  32.         cout<<array[i]<<"  ";  
  33.     cout<<endl;  
  34.     return 0;  
  35. }  
   

 

四.shell sorting

增量为2的shell排序的时间代价可以达到θ(n的3/2次方),有的增量可以达到θ(n的7/6次方),很接近θ(n)。

 

[cpp] view plaincopyprint?
  1. #include <iostream>  
  2. using namespace std;  
  3.   
  4. /*交换函数,作用是交换数组中的两个元素的位置*/  
  5. void swap(int array[],int i,int j)  
  6. {  
  7.     int tmp=array[i];  
  8.     array[i]=array[j];  
  9.     array[j]=tmp;  
  10. }  
  11.   
  12. /*希尔排序*/  
  13. void ShellSort(int array[],int n)  
  14. {  
  15.     for(int delta=n/2;delta>0;delta/=2)  
  16.     {  
  17.         for(int i=0;i<delta;i++)  
  18.         {  
  19.             for(int j=i+delta;j<n;j+=delta)  
  20.             {  
  21.                 for(int k=j;k>0;k-=delta)  
  22.                 {  
  23.                     if(array[k]<array[k-1])  
  24.                         swap(array,k,k-1);  
  25.                 }  
  26.             }  
  27.         }  
  28.     }  
  29. }  
  30.   
  31. int main()  
  32. {  
  33.     int array[8]={6,8,7,3,1,2,5,4};  
  34.     ShellSort(array,8);  
  35.     for(int i=0;i<8;i++)  
  36.         cout<<array[i]<<"  ";  
  37.     cout<<endl;  
  38.     return 0;  
  39. }  
   

 

五.快速排序(quick sorting)

快速排序的最大时间代价为θ(n²),最小时间代价为θ(n*logn),平均时间代价为θ(n*logn)。注意:快速排序是一种不稳定的排序方式,其性能依赖于原始数组的有序程度,更进一步分析,就是依赖与轴值元素的选择。快排的比较次数远多于移动次数,所以主要考虑比较次数。
 快排中,每一次比较可以确定一个轴值元素的位置。若p[m,q,n](q为轴值元素)。当然确定第一个轴值元素也是要比较(n-m-1)次。但第二个轴值元素,第三个轴值元素就要进行(q-m-1)和(n-q-1)次比较。如果q的值若为m或n,快速排序就退化成冒泡排序了,快排就没有什么优势了。

 

[cpp] view plaincopyprint?
  1. #include <iostream>  
  2. using namespace std;  
  3.   
  4. /*交换函数,作用是交换数组中的两个元素的位置*/  
  5. void swap(int array[],int i,int j)  
  6. {  
  7.     int tmp=array[i];  
  8.     array[i]=array[j];  
  9.     array[j]=tmp;  
  10. }  
  11.   
  12. /*将轴值放到数组的适当的位置*/  
  13. int partition(int array[],int left,int right)  
  14. {  
  15.     int mid=(left+right)/2;  
  16.     int tmp=array[mid];  
  17.     swap(array,mid,right);  
  18.     int i=left;  
  19.     int j=right;  
  20.     while(1)  
  21.     {  
  22.         /*i指针向右移动,直到找到一个大于轴值的值*/  
  23.         while(1)  
  24.         {  
  25.             /*如果i与j相遇则确定轴值位置,将其返回*/  
  26.             if(i==j)  
  27.             {  
  28.                 array[i]=tmp;  
  29.                 return i;  
  30.             }  
  31.             if(array[i]>tmp)  
  32.             {  
  33.                 array[j]=array[i];  
  34.                 j--;  
  35.                 break;  
  36.             }  
  37.             i++;  
  38.         }  
  39.         /*j指针向左移动,直到找到一个小于轴值的值*/  
  40.         while(1)  
  41.         {  
  42.             /*如果i与j相遇则确定轴值位置,将其返回*/  
  43.             if(i==j)  
  44.             {  
  45.                 array[j]=tmp;  
  46.                 return j;  
  47.             }  
  48.             if(array[j]<tmp)  
  49.             {  
  50.                 array[i]=array[j];  
  51.                 i++;  
  52.                 break;  
  53.             }  
  54.             j--;  
  55.         }  
  56.     }  
  57. }  
  58.   
  59. /*快速排序*/  
  60. void quickSort(int array[],int left,int right)  
  61. {  
  62.     if(right<=left)  
  63.         return;  
  64.     int pivot=partition(array,left,right);  
  65.     quickSort(array,left,pivot-1);  
  66.     quickSort(array,pivot+1,right);  
  67. }  
  68.   
  69. int main()  
  70. {  
  71.     int array[8]={6,8,7,3,1,2,5,4};  
  72.     quickSort(array,0,7);  
  73.     for(int i=0;i<8;i++)  
  74.         cout<<array[i]<<"  ";  
  75.     cout<<endl;  
  76.     return 0;  
  77. }  
  

 

六.归并排序(merge sorting)

归并排序的最大时间代价,最小时间代价和平均时间代价均为θ(n*logn)。归并排序不依赖于原始数组的有序程度。

 

[cpp] view plaincopyprint?
  1. #include <iostream>  
  2. using namespace std;  
  3.   
  4. /*归并过程--将两个有序数组合并成一个有序数组*/  
  5. void merge(int array[],int tempArray[],int left,int right,int middle)  
  6. {  
  7.     int index1=left;  
  8.     int index2=middle+1;  
  9.     for(int i=left;(index1<=middle)&&(index2<=right);i++)  
  10.     {  
  11.         if(array[index1]<array[index2])  
  12.             tempArray[i]=array[index1++];  
  13.         else  
  14.             tempArray[i]=array[index2++];  
  15.     }  
  16.     while(index1<=middle)  
  17.         tempArray[i++]=array[index1++];  
  18.     while(index2<=right)  
  19.         tempArray[i++]=array[index2++];  
  20.     for(i=left;i<=right;i++)  
  21.         array[i]=tempArray[i];    
  22. }  
  23.   
  24. /*两路归并排序--array[]为待排数组,tempArray[]为临时数组,left和right分别是数组两端*/  
  25. void mergeSort(int array[],int tempArray[],int left,int right)  
  26. {  
  27.     if(left<right)  
  28.     {  
  29.         int middle=(left+right)/2;  
  30.         mergeSort(array,tempArray,left,middle);  
  31.         mergeSort(array,tempArray,middle+1,right);  
  32.         merge(array,tempArray,left,right,middle);  
  33.     }  
  34. }  
  35.   
  36. int main()  
  37. {  
  38.     int array[8]={6,8,7,3,1,2,5,4};  
  39.     int tempArray[8];  
  40.     mergeSort(array,tempArray,0,7);  
  41.     for(int i=0;i<8;i++)  
  42.         cout<<array[i]<<"  ";  
  43.     cout<<endl;  
  44.     return 0;  
  45. }  
  

 

七.堆排序(heap sorting)

堆排序的最大时间代价,最小时间代价和平均时间代价均为θ(n*logn)。堆排序和归并排序一样,不依赖于原始数组的有序程度。

 

HeapSorting.cpp

[cpp] view plaincopyprint?
  1. #include <iostream>  
  2. #include "MaxHeap.h"  
  3. using namespace std;  
  4.   
  5. /*最大堆排序函数*/  
  6. void heapSort(int array[],int n)  
  7. {  
  8.     MaxHeap max_heap=MaxHeap(array,n);  
  9.   
  10.     /*删除堆的最大值(堆顶),即每次将最大值与数组的最后一个元素交换位置*/  
  11.     for(int i=0;i<7;i++)  
  12.         max_heap.removeMax();  
  13. }  
  14.   
  15. int main()  
  16. {  
  17.     int array[8]={4,3,7,1,2,8,5,6};  
  18.     heapSort(array,8);  
  19.     for(int i=0;i<8;i++)  
  20.         cout<<array[i]<<"  ";  
  21.     cout<<endl;  
  22.     return 0;  
  23. }  
  

 

MaxHeap.h

 

[cpp] view plaincopyprint?
  1. #include <iostream>  
  2. using namespace std;  
  3.   
  4. /*最大堆定义*/  
  5. class MaxHeap  
  6. {  
  7. private:  
  8.     int size; //最大堆的元素数目  
  9. int * array;  //最大堆数组的首地址指针  
  10. public:  
  11.     MaxHeap(int array[],int n); //用已有数组初始化一个最大堆   
  12.          void buildHeap();   //构建最大堆  
  13.     void siftDown(int index);  //向下筛选法  
  14.     void swap(int index1,int index2);  //交换位置为index1与index2的元素  
  15.     void removeMax();  //删除堆顶的最大值--与数组最后一个元素交换位置并重新构建最大堆  
  16.     int leftChild(int index);  //返回左孩子的位置  
  17.     int rightChild(int index);  //返回右孩子的位置  
  18. };  
  

 

MaxHeap.cpp

 

[cpp] view plaincopyprint?
  1. #include <iostream>  
  2. #include "MaxHeap.h"  
  3. using namespace std;  
  4.   
  5. /*最大堆成员函数实现*/  
  6. MaxHeap::MaxHeap(int array[],int n)  
  7. {  
  8.     this->array=array;  
  9.     size=n;  
  10.     buildHeap();  
  11. }  
  12.   
  13. void MaxHeap::buildHeap()  
  14. {  
  15.     for(int i=size/2-1;i>=0;i--)  
  16.         siftDown(i);  
  17. }  
  18.   
  19. void MaxHeap::siftDown(int index)  
  20. {  
  21.     int max_index=leftChild(index);  
  22.     while(max_index<size)  
  23.     {  
  24.         if(max_index<size-1&&array[rightChild(index)]>array[max_index])  
  25.             max_index++;  
  26.         if(array[index]>array[max_index])  
  27.             break;  
  28.         swap(index,max_index);  
  29.         index=max_index;  
  30.         max_index=leftChild(index);  
  31.     }  
  32. }  
  33.   
  34. void MaxHeap::swap(int index1,int index2)  
  35. {  
  36.     int temp=array[index1];  
  37.     array[index1]=array[index2];  
  38.     array[index2]=temp;  
  39. }  
  40.   
  41. void MaxHeap::removeMax()  
  42. {  
  43.     swap(0,size-1);  
  44.     size--;  
  45.     siftDown(0);  
  46. }  
  47.   
  48. int MaxHeap::leftChild(int index)  
  49. {  
  50.     return index*2+1;  
  51. }  
  52.   
  53. int MaxHeap::rightChild(int index)  
  54. {  
  55.     return index*2+2;  
  56. }  
  

 

八.基数排序(radix sorting)

基数排序的时间复杂度为O (nlog(r)m),其中r为所采取的基数,而m为堆数,在某些时候,基数排序法的效率高于其它的比较性排序法。基数排序法是属于稳定性的排序。

 

[cpp] view plaincopyprint?
  1. #include <iostream>  
  2. using namespace std;  
  3.   
  4. /*计算关键字位数的最大值*/  
  5. int KeySize(int array[],int size)  
  6. {  
  7.     int key_size=1;  
  8.   
  9.     for(int i=0;i<size;i++)  
  10.     {  
  11.         int temp=1;  
  12.         int n=10;  
  13.         while(array[i]/n>0)  
  14.         {  
  15.             temp++;  
  16.             n*=10;  
  17.         }  
  18.         key_size=(temp>key_size)?temp:key_size;  
  19.     }  
  20.     return key_size;  
  21. }  
  22.   
  23. /*基数排序*/  
  24. void RadixSort(int array[],int size)  
  25. {  
  26.     int bucket[10][10]={0};//定义基数桶  
  27.     int order[10]={0};//保存每个基数桶之中的元素个数  
  28.     int key_size=KeySize(array,size);//计算关键字位数的最大值  
  29.       
  30.     for(int n=1;key_size>0;n*=10,key_size--)  
  31.     {  
  32.         /*将待排序的元素按照关键值的大小依次放入基数桶之中*/  
  33.         for(int i=0;i<size;i++)  
  34.         {  
  35.             int lsd=(array[i]/n)%10;  
  36.             bucket[lsd][order[lsd]]=array[i];  
  37.             order[lsd]++;  
  38.         }  
  39.   
  40.         /*将基数桶中的元素重新串接起来*/  
  41.         int k=0;  
  42.         for(i=0;i<10;i++)  
  43.         {  
  44.             if(order[i]!=0)  
  45.             {  
  46.                 for(int j=0;j<order[i];j++)  
  47.                 {  
  48.                     array[k]=bucket[i][j];  
  49.                     k++;  
  50.                 }  
  51.                 order[i]=0;  
  52.             }  
  53.         }  
  54.     }  
  55. }  
  56.   
  57. int main()  
  58. {  
  59.     int array[10]={73,22,93,43,55,14,28,65,39,81};  
  60.     int size=sizeof(array)/sizeof(int);  
  61.     RadixSort(array,size);  
  62.     for(int i=0;i<size;i++)  
  63.         cout<<array[i]<<"  ";  
  64.     cout<<endl;  
  65.     return 0;  
  66. }  
   

 

 

     下面我们来讨论一下各种排序算法的稳定度,稳定排序算法会依照相等的关键(换言之就是值)维持纪录的相对次序。也就是一个排序算法是稳定的,就是当有两个有相等关键的纪录R和S,且在原本的串列中R出现在S之前,在排序过的串列中R也将会是在S之前。

  一般的方法:插入、交换、选择、合并等等。交换排序包含冒泡排序(bubble sort)和快速排序(quicksort)。选择排序包含shaker排序和堆排序(heapsort)。

  当相等的元素是无法分辨的,比如像是整数,稳定度并不是一个问题。然而,假设以下的数对将要以他们的第一个数字来排序。

  (4, 1) (3, 1) (3, 7) (5, 6)

  在这个状况下,有可能产生两种不同的结果,一个是依照相等的键值维持相对的次序,而另外一个则没有:

  (3, 1) (3, 7) (4, 1) (5, 6) (维持次序)

  (3, 7) (3, 1) (4, 1) (5, 6) (次序被改变)

  不稳定排序算法可能会在相等的键值中改变纪录的相对次序,但是稳定排序算法从来不会如此。不稳定排序算法可以被特别地时作为稳定。作这件事情的一个方式是人工扩充键值的比较,如此在其他方面相同键值的两个物件间之比较,就会被决定使用在原先资料次序中的条目,当作一个同分决赛。然而,要记住这种次序通常牵涉到额外的空间负担。

 

稳定的排序算法:

      冒泡排序(bubble sort) — O(n2)

  鸡尾酒排序 (Cocktail sort, 双向的冒泡排序) — O(n2)

  插入排序 (insertion sort)— O(n2)

  桶排序 (bucket sort)— O(n); 需要 O(k) 额外 记忆体

  归并排序 (merge sort)— O(n log n); 需要 O(n) 额外记忆体

  原地归并排序 — O(n2)

  二叉树排序 (Binary tree sort) — O(n log n); 需要 O(n) 额外记忆体

  基数排序 (radix sort)— O(n·k); 需要 O(n) 额外记忆体

不稳定的排序算法:

      选择排序 (selection sort)— O(n2)

    希尔排序 (shell sort)— O(n log n) 如果使用最佳的现在版本

  Comb sort — O(n log n)

  堆排序 (heapsort)— O(n log n)

  Smoothsort — O(n log n)

  快速排序 (quicksort)— O(n log n) 期望时间, O(n2) 最坏情况; 对於大的、乱数串列一般相信是最快的已知排序

 

      一般来说:存在不相邻交换的排序算法是不稳定的,相邻交换的排序算法是稳定的;对于相邻交换的稳定排序算法,通过控制交换条件可以转换成不稳定排序算法;冒泡、插入、归并和基数排序是稳定的;选择、快速、希尔和堆排序是不稳定的。

原创粉丝点击