排序算法总结
排序算法总结
从六月初开始看算法导论,陆陆续续看了有2个月了,但实际看的时间只有半个月左右。这期间都忙着找导师、期末考试,同时还回家修养了十来天。真正专心的看算法是在离家返校后,由于没有考试和作业的烦恼,天天都沉浸在算法中,感觉效率较高。这段时间学到的东西较多,下面来总结一下:
学到的排序算法可以分为两类:比较排序、非比较排序。(这些排序算法的详细介绍及c程序实现在本文末都给出了链接,欢迎参考与指正!)
比较排序有:插入排序法、合并排序法、堆排序法、冒泡排序法、选择排序法、快速排序法等等、
非比较排序有:计数排序法、基数排序法、桶排序法。
下面以我的理解来分别说说它们各自的特点。不过在此之前,我先阐述两个概念: 原地排序 、 稳定排序
原地排序:如果对数组A进行排序,则这些数据是在A中进行重新排序的,在任何时候,至多只有其中的常数个数字是存储在数组之外的。
稳定排序:原数组中相同元素的相对位置(即前后关系)在经过排序后仍然不变。例如a[5]={3,2,3,1,5},经过稳定排序后为:a[5]={1,2,3,3,5}.
谁最实用?对于较短的数组,比如说元素个数才十几二十个,用冒泡排序法就可以了,因为我们大多数人对冒泡排序法可谓是信手拈来,可以不假思索就可以写出,尽管其时间复杂度比较大。当然快速排序法是非常值得推荐的,他的适用范围很广,用的十分普遍。
插入排序:时间复杂度:O(n*n).是原定排序,稳定排序(在本文中所有说稳定排序的意思是可以是稳定排序,对于是否是稳定排序,还取决于具体的程序,看进行数据间比较时是否带等号,但是快速排序肯定不是稳定排序)。其实我们玩斗地主,摸牌的过程就是在进行插入排序!当原始数组为正序排列时,所用时间最短;当原始数组为逆序排序时,所用时间最长。所以插入排序适合于原数组大多数元素已经排好的情况。(所谓正序、逆序只是相对的概念,如果要对数组从小到大排序,则如果原数组本身是从大到小排列的,则称为逆序,否则称为正序)。
合并排序:时间复杂度:O(nlogn).不是原地排序,可以是稳定排序。它和快速排序一样是基于分而治之的思想的(稍有不同),但是它比快速排序要快一点。尽管如此,由于它不是原地排序,需要占用较多空间,所以运用没有快速排序广泛。它和插入排序相比,它的最坏运行情况是O(nlogn),插入排序的最坏运行情况为O(n*n),但插入排序中的常数因子使得在n比较小时,运行要更快一点。
冒泡排序:时间复杂度为:O(n*n),原地排序,稳定排序。由于平常遇到的排序都很简单,并且大家可以对冒泡排序信手拈来,因此用的十分普遍。
选择排序:时间复杂度为:O(n*n),原地排序,稳定排序。实际上,选择排序和冒泡排序的思想上是一致的,只是在具体操作中使用了一点技巧,避免了数据的多次交换,因此运行时间比冒泡排序快一点。
堆排序:时间复杂度:O(nlogn).是原地排序。它是将插入排序和合并排序的优点集于一身。像插入排序一样是原地排序,像合并排序一样运行时间为O(nlogn).虽然堆排序是一个漂亮的算法,但是实际中,快速排序的一个好的实现往往要优于堆排序。但是堆有个很常见的应用:作为高效的优先级队列。
快速排序:时间复杂度:O(nlogn),不稳定排序,原地排序。最坏的运行时间为O(n*n),它的运行时间与划分的对称性有关。加入随机化后,在平均情况下,划分就能比较均匀,从而就能获得较好性能。
由定理可知,比较排序时间复杂度最好为O(nlogn),在上述的比较排序算法中,时间复杂度为O(nlogn)较好的算法有:合并排序、堆排序、快速排序。其中快速排序运用最广泛,因为合并排序不是原地排序,需占用较多空间,堆排序的数据交换比较多。
计数排序:O(n),稳定排序、不是原地排序。其思想比较独特,运用条件是数组的元素的值域在某一个范围内,并且由于不是原地排序,需要占用额外的内存,所以运用不是很广。
基数排序:O(n),稳定排序、不是原地排序。基数排序就是不断地调用计数排序。其思想比较新颖,在进行比较两个数的大小是反其道而行之,先从个位数开始比较。相比于从高位开始比较,它避免了记录中间过程的值。
桶排序:O(n),稳定排序、不是原地排序。也是基于分而治之的思想,先将序列进行分类,然后分别处理,最后再合并。与合并算法不同的是,合并的时候不需要进行比较。
上述的非比较排序中,其时间复杂度都是线性的,由于是非比较的排序,所以比较排序算法的下界O(nlogn)就不适用了。他们都只有在一些特定的条件下使用,因此运用不是很广泛,但是这些思想却是值得我们学习的。
下面附上上述各个排序算法的详细介绍及C程序实现的链接,欢迎各位拍砖!
比较排序: 插入排序 合并排序 堆排序 冒泡排序 选择排序 快速排序