多种排序算法的比较

1.冒泡排序和选择排序

为什么把冒泡排序和选择排序放在一块儿呢?因为我发现他们两个有点像。

冒泡排序是不停的把最大的元素换到数组的最右端。

而选择排序是把最小的元素换到最左端。

看到这儿,你是不是觉得冒泡和选择好像没啥区别啊,把最大换成最小就成了一种新的算法?那我也来一个?

其实,无论换最大还是最小,都无关紧要,就算冒泡变成换最小的元素换到数组的最左端,那它也叫冒泡排序的。

 

冒泡排序的描述:它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到不需要交换,也就是说该数列已经排序完成。

选择排序的描述:每一趟从待排序的数据元素中选出最小(或最大)的一个元素,顺序放在已排好序的数列的最后,直到全部待排序的数据元素排完。

为了便于描述二者之间的区别,我们把冒泡和排序都换成是把最大元素换到最右端。

他们的最大区别在于

冒泡排序不停比较两个相邻的元素,并不断交换较大的元素,直到数组尾。

选择排序开始只比较不交换,直到数组尾才进行一次交换。

在这里,你可以认为选择排序是冒泡排序的优化,因为选择排序多数时候减少了交换的次数,却实现了相同的效果。

难道冒泡排序多做的那些交换就全是无用功吗?仔细想想,当然不是这样!

仔细看看冒泡排序是怎么说的,“走访数列的工作是重复地进行直到不需要交换”,这个很重要,假如我们加入一个变量,来记录交换次数,当某趟遍历交换次数为零的时候,说明数组已经排序好了,那就可以结束排序了!

比如“1,2,3,4,5,6,7,8,9”,对于这样一个数组,冒泡排序只需要进行一次遍历就ok了,所以对于基本有序的数组,冒泡可以做到O(n)的复杂度。

但是选择排序做不到这一点,一个已经有序的数组和随机排列的数组将花差不多的时间(稍微会少点,因为有序数组不需要交换,只需要比较)。

在这里,我们发现,选择排序并没有利用数组的初始输入状态,而冒泡排序利用了。这是冒泡的优势,在这里,你可能会想,冒泡是如何利用初始状态的呢?当然就是在一次又一次相邻元素的比较上嘛。

最后,由于选择排序会交换不相邻元素,比如直接把a[0]和a[8]交换位置,导致了选择排序的不稳定。而冒泡只会交换相邻元素,所以冒泡是稳定的,我们当然不会蠢到a[0]=1,a[1]=1的时候交换他们的位置是吧。

待会儿我们会分析插入排序和希尔排序,这两个排序的稳定性的因果分析也和是否交换不相邻元素有关。

先到这儿,明天再写。。

 

=====================我是分割线8.18===============================

上次说好了明天再写,结果那天晚上就被人分手了,真是悲痛万分。话说爱情从未影响过学习,影响学习的是暗恋和失恋,这真是一个真理啊。

学习还是要继续,现在开始吧。

=====================我也是分割线=================================

2.插入排序和希尔排序

插入排序:插入排序的基本操作就是将一个数据插入到已经排好序的有序数据中,并且要求插入后此数据序列仍然有序。

比如序列1235678,现在又来了一个4,那我们就要把它插到3和5之间。举个例子,你扑克的时候一般手上的牌都是排好序的,你又起了一张牌,肯定会把它插入到合适的位置(也就是不会破坏有序的位置)。

不过这里有点小小的区别,人眼可以一瞬间看出牌应该插在哪里,但是计算机做不到,计算机得一个个比较。我们如果想把4插到1235678的序列中,得按顺序和8,7,6,5,3进行比较,然后发现了3比4小,所以把4插到3后面。

正因为这样,插入排序的时间复杂度是O(n^2),不过假如数组基本有序,你可以发现,插入排序的时间复杂度将下降很多。对于一个有序数组,插入排序的时间复杂度是O(n)。

也正因为这样,插入排序对于小数组的表现是不错的,因为小数组基本有序的可能性是比较大的,即便是无序,因为数组小,时间复杂度也不会很大。

实践中表明,当快速排序递归到小数组的时候(元素数目15以下),用插入排序,可以使效率增高20%到30%。(来源:《算法》)

由于插入排序也是通过一次又一次的比较来排序的,并且也不会交换不相邻的元素。所以插入排序是稳定的。

 

希尔排序:希尔排序是插入排序的改良版,是一种分组插入排序。

所有距离为d1的倍数的记录放在同一个组中进行插入排序,然后,取第二个增量d2<d1重复上述的分组和排序,直至所取的增量dt=1(dt<dt-l<…<d2<d1),即所有记录放在同一组中进行直接插入排序为止。

那么到底希尔排序改良在哪儿呢?

作为一个分组插入排序,它每次会比较相距dt的元素,如果顺序不合,就会交换它。

这有什么好处呢?我们知道,插入排序只会比较和交换相邻的元素,这样会很浪费,假如某元素正确的位置在序列中间,那它就会比较很多次。

现在希尔排序排序提供一个好的方式,就是先以大距离dt分组进行排序,这样,然后不断减小dt,直到dt为1。

这样的话,元素在分组排序的时候,会交换远距离元素,也能离正确的位置更近,但却只需要少数几次交换。

这有点不好理解,我们举个例子,假如有一个序列,1,6,8,4,3,9,0,2,5,7。先取分组距离为3,那么1,4,0,7,是一组,排好序之后变成0,1,4,7.一共只需要进行四次比较,可如果我们只比较相邻元素的话,那么就远远不知四次了。

有人可能会问,这样排序一次之后,真的比初始序列更加有序了吗,所获得的收益划得来吗?

直观的想,分组之后,我们把小的排在了大的前面,应该就是更接近有序了。可能会有部分分组没有那么好的效果,但从总体上看,就会是更加有序了,应该有数学证明的,我待会儿找找。

我有一个证明的思路,就是把初始序列中所有元素离正确位置的距离加起来,再把一次排序后离正确位置的距离加起来,相减,再和比较次数进行比较。

另外,dt的取值也很关键,这是影响希尔排序的关键因素,不过我一般取d1=n/3+1;d2=d1/3+1;d3=d2/3+1;..............

看到这儿,有没有似曾相识?是的,二分法也是这么做的,不需要一次就找到那个元素,不断缩小寻找范围,直到找到那个元素。

希尔排序当然是不稳定的,因为交换了不相邻的元素。

今天就到这儿吧,明天再来!

 

posted @ 2014-08-09 16:45  ian在这儿  阅读(207)  评论(0编辑  收藏  举报