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



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

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

 

一.插入排序(insert sorting)

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

 

 

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

 

 

二.冒泡排序(bubble sorting)

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

 

 

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

 

 

三.选择排序(selection sorting)

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

 

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

 

 

四.shell sorting

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

 

 

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

 

 

五.快速排序(quick sorting)

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

 

 

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

 

 

六.归并排序(merge sorting)

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

 

 

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

 

 

七.堆排序(heap sorting)

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

 

HeapSorting.cpp

 

 

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

 

 

MaxHeap.h


 

 

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

 

 

MaxHeap.cpp

 

 

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

 

 

八.基数排序(radix sorting)

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

 

 

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

 

 

 

下面我们来讨论一下各种排序算法的稳定度,稳定排序算法会依照相等的关键(换言之就是值)维持纪录的相对次序。也就是一个排序算法是稳定的,就是当有两个有相等关键的纪录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) 最坏情况; 对於大的、乱数串列一般相信是最快的已知排序

 

 

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

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