各种排序的实现与效率分析
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
各种排序的实现与效率分析
一、排序原理
(1)直接插入排序
基本原理:这是最简单的一种排序方法,它的基本操作是将一个记录插入到已排好的有序表中,从而得到一个新的、记录增1的有序表。
效率分析:该排序算法简洁,易于实现。从空间来看,他只需要一个记录的辅助空间,即空间复杂度为O(1).从时间来看,排序的基本操作为:比较两个关键字的大小和移动记录。当待排序列中记录按关键字非递减有序排列(即正序)时,所需进行关键字间的比较次数达最小值n-1,记录不需移动;反之,当待排序列中记录按关键字非递增有序排列(即逆序)时,总的比较次数达最大值(n+2)(n-1)/2,记录移动也达到最大值(n+4)(n-2)/2.由于待排记录是随机的,可取最大值与最小值的平均值,约为n²/4.则直接插入排序的时间复杂度为O(n²).由此可知,直接插入排序的元素个数n越小越好,源序列排序度越高越好(正序时时间复杂度可提高至O(n))。插入排序算法对于大数组,这种算法非常慢。但是对于小数组,它比其他算法快。其他算法因为待的数组元素很少,反而使得效率降低。插入排序还有一个优点就是排序稳定。
(2)折半插入排序
基本原理:折半插入是在直接插入排序的基础上实现的,不同的是折半插入排序在将数据插入一个有序表时,采用效率更高的“折半查找”来确定插入位置。
效率分析:由上可知该排序所需存储空间和直接插入排序相同。从时间上比较,折半插入排序仅减少了关键字间的比较次数,为O(nlogn)。而记录的移动次数不变。因此,折半查找排序的时间复杂度为O(nlogn)+O(n²)
=O(n²)。排序稳定。
(3)希尔排序
基本原理:希尔排序也一种插入排序类的方法,由于直接插入排序序列越短越好,源序列的排序度越好效率越高。Shell根据这两点分析结果进行了改进,将待排记录序列以一定的增量间隔dk分割成多个子序列,对每个子序列分别进行一趟直接插入排序,然后逐步减小分组的步长dk,对于每一个步长dk下的各个子序列进行同样方法的排序,直到步长为1时再进行一次整体排序。因为不管记录序列多么庞大,关键字多么混乱,在先前较大的分组步长dk下每个子序列的规模都不大,用直接插入排序效率都较高。尽管在随后的步长dk递减分组中子序列越来越大,但由于整个序列的有序性也越来越明显,则排序效率依然较高。这种改进抓住了直接插入排序的两点本质,大大提高了它的时间效率。
效率分析:希尔排序有以下几个关键特性:
(1)希尔排序的核心是以某个增量dk为步长跳跃分组进行插入排序,由于分组的步长dk 逐步缩小,所以也叫“缩小增量排序”插入排序。其关键是如何选取分组的步长序列才能使得希尔方法的时间效率最高;
(2)待排序列记录的个数n、跳跃分组步长逐步减小直到为1时所进行的扫描次数T、增量的和、记录关键字比较的次数以及记录移动的次数或各子序列中的反序数等因素都影响希尔算法的时间复杂度:其中记录关键字比较的次数是重要因素,它主要取决于分组步长序列的选择;
(3)希尔方法是一种不稳定排序算法,因为其排序过程中各趟的步长不同,在第k遍用dk 作为步长排序之后,第k+1遍排序时可能会遇到多个逆序存在,影响排序的稳定性。
(3)冒泡排序
基本原理:冒泡排序分为若干趟进行,每一趟排序从前往后比较每两个相邻的元素的大小(因此一趟排序要比较n-1对位置相邻的数)并在每次发现前面的那个数比紧接它后的数大时交换位置;进行足够多趟直到某一趟跑完后发现这一趟没有进行任何交换操作(最坏情况下要跑n-1趟,这种情况在最小的数位于给定数列的最后面时发生)。事实上,在第一趟冒泡结束后,最后面那个数肯定是最大的了,于是第二次只需要对前面n-1个数排序,这又将把这n-1个数中最大的数放到整个数列的倒数第二个位置。这样下去,冒泡排序第i 趟结束后后面i个数都已经到位了,第i+1趟实际上只考虑前n-i个数(需要的比较次数比前面所说的n-1要小)。
效率分析:冒泡排序在给出的序列为正序排列时是最好的情况,这时每一次比较都不需要要进行交换操作。因此冒泡排序最好情况下需要交换0次。给出的序列逆序排列是最坏的情况,这时每一次比较都要进行交换操作。一次交换操作需要3次赋值实现,因此冒泡排序最坏情况下需要赋值3n(n-1)/2次。比较次数方面,无论数据如何,每次排序均要比较n(n-1)/2次。
(4)快速排序
基本原理:快速排序是对冒泡排序的一种改进,它的基本思想是,通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。
快速排序采用了分治法的思想,把大的问题分解为同类型的小问题。一般分如下步骤:(1)选择一个中枢元素(有很多选法,我的实现里使用第一个元素为中枢的简单方法)(2)以该中枢元素为基准点,将小于中枢的元素放在中枢后集合的前部分,比它大的在集合后部分,待集合基本排序完成后(此时前部分元素小于后部分元素),把中枢元素放在合适的位置。
(3)根据中枢元素最后确定的位置,把数组分成三部分,左边的,右边的,枢纽元素自己,对左边的,右边的分别递归调用快速排序算法即可。
这里的重点与难点在于第二步,这一步的方法是以第一个元素为中枢元素,刚开始时使用低指针指向中枢元素。当中枢元素在低指针位置时,此时我们判断高指针指向的元素是否小于中枢元素,如果大于中枢元素则高指针继续向头移动,如果小于则与中枢元素交换,此时中枢元素被移到了高指针位置;当中枢元素在高指针位置时,我们此时判断低指针指向的元素是否大于中枢元素,如果小于中枢元素则低指针继续向尾移动,如果大于则与中枢元素交换,此时中枢元素又回到了低指针位置;这时是拿高还是低指针所指向的元素与中枢比较时根据前面逻辑来处理,直到高低指针指向同一位置则完成一轮排序,然后再对中枢元素两边的序列进行同样的操作直到排序完成
效率分析:快速排序的平均时间为kn ln(n),其中n为待排时间中记录的个数,k为某个常数,经验证明,在所有同数量级的此类排序方法中,快速排序的常数因子k最小。因此,就平均时间而言,快速排序时最好的一种内部排序。快速排序在最好的情况时是正序,比较次数和移动次数均为O(n ln(n)),最坏状况下,比较次数和移动次数均为n(n-1)/2次。
(5)简单选择排序
基本原理:每一趟从待排序的数据元素中选出最小(或最大)的一个元素,顺序放在已排好序的数列的最后,直到全部待排序的数据元素排完。
选择排序不像冒泡排序算法那样先并不急于调换位置,第一轮(i=1)先从array[i]开始逐个检查,看哪个数最小就记下该数所在的位置于min中,等一轮扫描完毕,如果找到比array[i-1]更小的元素,则把array[min]和a[i-1]对调,这时array[i]到最后一个元素中最小