常用的排序算法总结

插入 排序 的基本思想是每次将一个待排序的记录,按其关键字大小插入到前面已经排好序的子文件中的适当位置,直到全部记录插入完成为止。常见的插入排序有插入排序(Insertion Sort),希尔排序(Shell Sort),二叉查找树排序(Tree Sort),图书馆排序(Library Sort),Patience排序(Patience Sort)。下面介绍前两种:
  • 直接插入排序
最差时间复杂度:O(n^2)          最优时间复杂度:O(n)           平均时间复杂度:O(n^2)                 稳定性:稳定
直接插入排序(Insertion Sort) ,是一种简单直观的排序 算法 。它的工作原理是通过构建有序序列,对未排序的数据,在已排序序列中从后向前扫描,找到相应位置并插入。
插入排序算法的一般步骤:
1.从第一个元素开始,该元素可以认为已被排序;
2.取出下一个元素,在已经排序的元素序列中从后向前扫描;
3.如果该元素(已排序)大于新元素,将该元素移到下一个位置;
4.重复步骤3,直到找到已排序的元素小于或者等于新元素的位置;
5.将新元素插入到该位置后,重复2~5


  1. void InsertionSort(int *a, int len)  
  2. {  
  3.     for (int j=1; j<len; j++)  
  4.     {  
  5.         int key = a[j];  
  6.         int i = j-1;  
  7.         while (i>=0 && a[i]>key)  
  8.         {  
  9.             a[i+1] = a[i];  
  10.             i--;  
  11.         }  
  12.         a[i+1] = key;  
  13.     }  
  14. }  
  • 希尔排序
平均时间复杂度:O(nlogn)                       稳定性:不稳定
希尔排序(Shell Sort) ,也称为递减增量排序算法,是插入排序的一种高速而稳定的改进版本。希尔排序是基于插入排序的以下两点性质而提出改进方法的:1.插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率;2.但插入排序一般来说是低效的, 因为插入排序每次只能将数据移动一位。
希尔排序的一般步骤为:
1.先取一个小于n的整数d1作为第一个增量,把文件的全部记录分成d1个组。所有距离为dl的倍数的记录放在同一个组中,在各组内进行直接插人排序。
2.取第二个增量d2<d1重复上述的分组和排序,直至所取的增量dt=1(dt<dt-l<…<d2<d1),即所有记录放在同一组中进行直接插入排序为止。
步长的选择是希尔排序的重要部分。只要最终步长为1任何步长串行都可以工作。算法最开始以一定的步长进行排序。然后会继续以一定步长进行排序,最终算法以步长为1进行排序。当步长为1时,算法变为插入排序,这就保证了数据一定会被排序。

  1. void ShellSort(int *a, int len)  
  2. {  
  3.     int h = 1;  
  4.     while( h<len )   
  5.         h = 3*h + 1;  
  6.     while( h>0 )  
  7.     {  
  8.         for (int j=h; j<len; j++)  
  9.         {  
  10.             int key = a[j];  
  11.             int i = j-h;  
  12.             while( i>=0 && a[i]>key )  
  13.             {  
  14.                 a[i+h] = a[i];  
  15.                 i = i-h;  
  16.             }  
  17.             a[i+h] = key;  
  18.         }  
  19.         h  = h/3;  
  20.     }  
  21. }  
  • 快速排序
最差时间复杂度  O(n^2)  最好时间复杂度  O(nlogn)    平均时间复杂度O(nlogn)     不稳定
由C. A. R. Hoare在1962年提出,该算法是目前实践中使用最频繁,实用高效的最好排序算法,
快速排序算法是采用分治思想的算法,算法分三个步骤
  1. 从数组中抽出一个元素作为基数v(我们称之为划界元素),一般是取第一个、最后一个元素或中间的元素
  2. 将剩余的元素中小于v的移动到v的左边,将大于v元素移动到v的右边
  3. 对左右两个分区重复以上步骤直到所有元素都是有排序好
int     partition( int   a[],   int   p,   int   r) {
     int   key = a[r]; //取最后一个
     int   i = p - 1;
     for   ( int   j = p; j < r; j++)
     {  
         if   (a[j] <= key)
         {          
             i++;
             //i一直代表小于key元素的最后一个索引,当发现有比key小的a[j]时候,i+1 后交换         
             exchange(&a[i], &a[j]);
         }      
     }  
     exchange(&a[i + 1], &a[r]); //将key切换到中间来,左边是小于key的,右边是大于key的值。
     return   i + 1;
}
void   quickSort( int   a[],   int   p,   int   r) {
     int   position = 0;
     if   (p<r)
     {
         position = partition(a,p,r); //返回划分元素的最终位置
         quickSort(a,p,position-1); //划分左边递归
         quickSort(a, position + 1,r); //划分右边递归
     }  
}


选择 排序 算法 就是每一趟从待排序的记录中选出关键字最小(最大)的记录,顺序放在已排好序的子文件的最后(最前),直到全部记录排序完毕。常见的选择排序有直接选择排序(Selection Sort),堆排序(Heap Sort)
最差时间复杂度:O(n^2)             最优时间复杂度:O(n^2)            平均时间复杂度:O(n^2)           稳定性:不稳定
直接选择排序(Selection Sort ),这是一种简单直观的排序算法。它首先在未排序序列中找到最小(大)元素,存放到排序序列的其起始位置,然后再从剩余未排序的序列元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素排序完毕。

  1. void SelectSort(int *a, int len)  
  2. {  
  3.     for (int i=0; i<len-1; i++)  
  4.     {  
  5.         int k = i;  
  6.         int key = a[i];  
  7.         for (int j=i+1; j<len; j++)  
  8.         {  
  9.             if (a[j]<key)  
  10.             {  
  11.                 k = j;  
  12.                 key = a[j];  
  13.             }  
  14.         }  
  15.         if (k!=i)  
  16.             swap(a[i], a[k]);  
  17.     }  
  18. }  
  • 堆排序堆排序是利用堆的性质进行的一种选择排序。
    最差时间复杂度:O(nlogn)            最优时间复杂度:O(nlogn)           平均时间复杂度:O(nlogn)          稳定性:不稳定
  • 堆:一棵完全二叉树,对于大根堆来说,其每一个节点的值都大于或者等于任意一个子节点的值。小根堆反之。
下面举例说明:
    给定一个整形数组a[]={16,7,3,20,17,8},对其进行堆排序。  首先根据该数组元素构建一个完全二叉树,得到
 
 然后需要构造初始堆,则从最后一个非叶节点开始调整,调整过程如下:
20 和16交换后导致16不满足堆的性质,因此需重新调整
这样就得到了初始堆。
即每次调整都是从父节点、左孩子节点、右孩子节点三者中选择最大者跟父节点进行交换(交换之后可能造成被交换的孩子节点不满足堆的性质,因此每次交换之后要重新对被交换的孩子节点进行调整)。有了初始堆之后就可以进行排序了。
3位于堆顶不满堆的性质,则需调整继续调整


下面的图也可以说明推排序的思想:

  1. voidHeapAdjust(int*array,inti,intsize)
  2. {
  3.        intlchild = 2 * i + 1;
  4.        intrchild = 2 * i + 2;
  5.        intmax = i;

  6.        if(lchild < size&&array[lchild] > array[max])
  7.        {
  8.               max = lchild;
  9.        }
  10.        if(rchild < size&&array[rchild] > array[max])
  11.        {
  12.               max = rchild;
  13.        }
  14.        if(max != i)
  15.        {
  16.               swap(array[i],array[max]);
  17.               HeapAdjust(array, max, size);
  18.        }
  19. }

  20. voidBuildHeap(int*array,intsize)
  21. {
  22.        for(inti = (size/ 2)-1; i >= 0; i--)
  23.               HeapAdjust(array, i, size);
  24.        for(inti = size - 1; i >= 1; i--)
  25.        {
  26.               swap(array[0],array[i]);
  27.               HeapAdjust(array, 0, i);
  28.        }
  29. }

  • 归并排序
最差时间复杂度:O(nlogn)             最优时间复杂度:O(nlogn)            平均时间复杂度:O(nlogn)           稳定性:稳定
归并排序是建立在归并操作上的一种有效的排序 算法 。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。
首先考虑下如何将将二个有序数列合并。这个非常简单,只要从比较二个数列的第一个数,谁小就先取谁,取了后就在对应数列中删除这个数。然后再进行比较,如果有数列为空,那直接将另一个数列的数据依次取出即可。
解决了上面的合并有序数列问题,再来看归并排序,其的基本思路就是将数组分成二组A,B,如果这二组组内的数据都是有序的,那么就可以很方便的将这二组数据进行排序。如何让这二组组内数据有序了?
可以将A,B组各自再分成二组。依次类推,当分出来的小组只有一个数据时,可以认为这个小组组内已经达到了有序,然后再合并相邻的二个小组就可以了。这样通过先递 的分解数列,再合 数列就完成了 归并 排序。
  1. void Merge(int * a, int low, int mid, int high, int * temp)
  2. {
  3.     int i,j,k;
  4.     i = low;
  5.     j = mid + 1;//避免重复比较a[mid]
  6.     k = 0;
  7.     while (i <= mid && j <= high)//数组a[low,mid]与数组(mid,high]均没有全部归入数组temp中去
  8.     {
  9.         if(a[i] <= a[j])        //如果a[i]小于等于a[j]
  10.             temp[k++] = a[i++]; //则将a[i]的值赋给temp[k],之后i,k各加一,表示后移一位
  11.         else
  12.             temp[k++] = a[j++]; //否则,将a[j]的值赋给temp[k],j,k各加一
  13.     }
  14.     while(i <= mid)             //表示数组a(mid,high]已经全部归入temp数组中去了,而数组a[low,mid]还有剩余
  15.         temp[k++] = a[i++];     //将数组a[low,mid]剩下的值,逐一归入数组temp
  16.     while(j <= high)           //表示数组a[low,mid]已经全部归入到temp数组中去了,而数组(mid,high]还有剩余
  17.         temp[k++] = a[j++];     //将数组a(mid,high]剩下的值,逐一归入数组temp

  18.     for (i = 0; i < k; i++)     //将归并后的数组的值逐一赋给数组a[low,high]
  19.         a[low+i] = temp[i];     //注意,应从a[low+i]开始赋值
  20. }

  21. //二路归并(递归实现)
  22. void MergeSort(int * a, int low, int high, int * temp)
  23. {
  24.     if (low < high)
  25.     {
  26.         int mid = (low + high)/2;
  27.         MergeSort(a,low,mid,temp);      //左边有序
  28.         MergeSort(a,mid+1,high,temp);   //右边有序
  29.         Merge(a,low,mid,high,temp);     //再将两个有序序列合并
  30.     }
  31. }
  • 计数排序
    最差时间复杂度:O(n)            最优时间复杂度:O(n)           平均时间复杂度:O(n)          稳定性:稳定
以往的排序 算法 中,各个元素的位置基于元素直接的比较,这类排序称为 比较排序 。任意一个比较排序算法在最坏情况下,都需要做Ω(nlgn)次的比较。 而计数排序是基于非排序的思想的,计数排序假设n个输入元素中的每一个都是介于0到k之间的整数。
计数排序的思想是对每一个输入元素x,确定出小于x的元素个数,有了这一信息,就可以把x直接放在它在最终输出数组的位置上,例如,如果有17个元素小于x,则x就是属于第18个输出位置。当几个元素相同是,方案要略作修改。 计数排序是 稳定 的。
  1. voidCountingSort(int*array,intlength)
  2. {
  3.        inttemp = array[0];
  4.        for(inti = 1; i < length; i++)
  5.        {
  6.               if(temp < array[i])
  7.                      temp =array[i];
  8.        }
  9.        intbucket[10] = { 0 };//注意,需要提前定义好bucket中的大小,待改进。
  10.        for(inti = 0; i <length; i++)
  11.               bucket[array[i]]++;
  12.        intj = 0;
  13.        for(inti = 0; i < temp + 1; i++)
  14.        {
  15.               while(bucket[i] > 0)
  16.               {
  17.                      array[j++] = i;
  18.                      bucket[i]--;
  19.               }
  20.        }
  21. }
常见的排序算法总结:


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值