排序算法的稳定性 排序算法之插入排序(Insertion Sort) 排序算法之希尔排序(Shell Sort) 排序算法之冒泡排序(Bubble Sort) 排序算法之快速排序(Quick Sort

主要的排序算法有八种:直接插入排序,希尔排序(这两种统称为插入排序),冒泡排序,快速排序(这两种统称为交换排序),直接选择排序,堆排序(这两种统称为选择排序),归并排序,基数排序。今天咱们就讨论一下它们各自的稳定性。若是对算法不熟悉,能够查看个人另外几篇博客,而后再来阅读。html

 

1、什么是算法稳定性算法

考察排序算法的时候有一个很重要的特性,就是算法的稳定性:假定在待排序的记录序列中,存在多个具备相同的关键字的记录,若通过排序,这些记录的相对次序保持不变,即在原序列中,ri=rj,且ri在rj以前,而在排序后的序列中,ri仍在rj以前,则称这种排序算法是稳定的;不然称为不稳定的。数组

 

2、算法稳定性的重要性post

算法稳定性为何这么重要呢?ui

1)在实际的应用中,咱们交换的不必定只是一个整数,而多是一个很大的对象,交换元素存在必定的开销;url

2)参照基数排序(后面会讲),不稳定排序是没法完成基数排序的,讲述完基数排序后,还会补充这里的缘由。设计

 

3、八大算法的稳定性code

1)直接插入排序@排序算法之插入排序(Insertion Sort)htm

其大体原理是:将数组分为无序区和有序区两个区,而后不断将无序区的第一个元素按大小顺序插入到有序区中去,最终将全部无序区元素都移动到有序区完成排序。对象

咱们假设一个数组,元素已经排序为{1,5A,7,5B,9},其中前面三个已经排序完成,后面没有排序,即前面三个是有序区,后面两个是无序区,如今要将无序区的5B插入到有序区,则若是咱们将元素插入到5A以前,咱们须要日后移动两个元素,若是插入到5A以后,则须要移动一个元素,所以咱们选择移动一个元素,而5A和5B也保持原来的顺序,于是直接插入排序是稳定的。

 

2)希尔排序@排序算法之希尔排序(Shell Sort)

其大体原理是:又称Gap缩小排序。先将序列按Gap划分为元素个数相同的若干组,使用直接插入排序法进行排序,而后不断缩小Gap直至为1,最后使用直接插入排序完成排序。希尔排序实际上是直接插入排序的加强版。

咱们来证实它是不稳定的,假设有一个数组{3,2A,2B,4},咱们要升序排列,按照算法,第一次Gap=2,便可以分为{3,2B}和{2A,4}两组,而后对每一组进行插入排序,能够排序成{2B,2A,3,4},第二次Gap=1,因为插入排序是稳定的,因此2A和2B不会交换顺序了。由此能够看到,希尔排序是不稳定的。

 

3)冒泡排序@排序算法之冒泡排序(Bubble Sort)

其大体原理是:将序列划分为无序和有序区,不断经过交换较大元素至无序区尾完成排序。

熟悉冒泡排序的人必定知道,冒泡排序经过不断的交换元素,将无序区的最大(最小)元素往无序区搬运,于是和插入排序同样,为了减小其交换次数,冒泡排序是稳定的。

 

4)快速排序@排序算法之快速排序(Quick Sort)

其大体原理是:不断寻找一个序列的中点,将小于该中点的元素搬移到中点左边,大于该中点的元素搬移到中点右边,或者反过来。而后对中点左右的序列递归的进行排序,直至所有序列排序完成,使用了分治的思想。

关于算法的稳定性有一点原本是打算后面再讲的,可是讲到快速排序就必定要说了。读者确定注意到了,前面的插入排序和冒泡排序彻底能够实现为不稳定算法,只是在比较元素决定是否交换的时候,是否加上等于号而已。快速排序更加显示了这一点,解释以下:

在算法导论里面,快速排序选择都是元素序列的最后一个元素,假设元素序列以下{3,9,5A,6,8,5B},这种状况下,和上面的状况一下,稳不稳定仍是看判断的时候是否出现等号,可是若是选择不是这样的,咱们假设一种特殊情况:{3,9,5A,5B,6,8,5C},算法的实现是选择中间的5B做为中点,则不论等号与否,都是不稳定的。实际上,算法导论的选择是很是有意义的,了解其算法过程的人能够看到,这样的选择极大的下降了交换元素的复杂度和移动元素的次数。算法导论中是加了等号的,即≤最后一个元素的值被移到了左边,于是快速排序是稳定的。

 

5)直接选择排序@排序算法之选择排序(Selection Sort)

其大体原理是:将序列划分为无序和有序区,寻找无序区中的最小值和无序区的首元素交换,有序区扩大一个,循环最终完成所有排序。

咱们仍是假设一个序列{1,3,5,10A,10B,7},看这个数列,假设前面三个是有序区,后面三个是无序区,则无序区中最小的元素是7,和无序区的首元素交换10A交换,则能够看到序列变成了{1,3,5,7,10B,10A},而后继续,无序区就剩下{10B,10A},咱们又能够看到,这里又是一个等号问题,一样,前面的交换是必然的,然后面的交换(若是等于也要交换)则不是必然的,为了减小元素交换,直接选择排序是不稳定的。

 

6)堆排序

其大体原理是:利用大根堆或小根堆思想,首先创建堆,而后将堆首与堆尾交换,堆尾以后为有序区。

考虑序列{9,5A,7,5B},按照堆排序的算法走一遍(算法导论中用的是最大堆,这个序列也是用最大堆来设计的),很快就能够发现,输出序列为{5B,5A,7,9},并且与等号无关,所以堆排序是不稳定的。

 

7)归并排序@排序算法之归并排序(Merge Sort)

其大体原理是:将原序列划分为有序的两个序列,而后利用归并算法进行合并,合并以后即为有序序列。

归并排序同样是稳定的,可是归并排序的稳定性并非为了减小元素交换次数,由于它的算法实现中没有元素交换这一律念。

 

8)基数排序

其大体原理是:将数字按位数划分出n个关键字,每次针对一个关键字进行排序,而后针对排序后的序列进行下一个关键字的排序,循环至全部关键字都使用过则排序完成。具体请参见:算法总结系列之五: 基数排序(Radix Sort)

基数排序对多个关键字进行排序,而且这些关键字仍是有优先级别的,对于整数来讲,位数越高的数字优先级越高,而基数排序则是对优先级低的先排序,所以,基数排序对于整数是从个十百千万一个个去排序的。注意,这里必须使用稳定排序,不然,就会让原先的地位排序成果毁于一旦,最终的不到正确的排序结果。

基数排序不过是一种思想,其每一位的排序都须要稳定算法,不然没法获得正确的结果。

 

3、总结

算法稳定性到底为何如此重要?上面提到的八种算法能够看到,其实不少算法都是能够实现稳定和不稳定两种情形的,那为何选择稳定?一个基本缘由就是减小元素交换次数,可是也有像归并排序这样的算法,与交换无关,那么稳定算法的意义在哪里呢?

稳定算法在单次排序的时候,意义并不显著,虽然上面提到减小元素交换,其实链表是能够避免这个消耗的,只不过操做比较复杂,其意义显示在基数排序中,即,咱们要对多个关键词屡次排序,这个时候,就必定要使用稳定算法。举一个现实的例子,好比排序的对象是人名,假设有如下两我的名:

Smith, Alfred
Smith, Zed

咱们先按first name排序,再按照last name排序,按照first name排序完成之后,就是上面的样子,再去按照last name排序,若是算法不稳定,则顺序极就会颠倒,是否是?这里的last name和first name彻底能够抽象成基数排序的不一样位,不是稳定算法,就不能获得正确结果。

相关文章
相关标签/搜索