排序算法

一.前言

排序在生活中比比皆是,每个学期评定奖学金时,首先要将学生按照各科成绩总和进行排名,然后选出符合条件的奖学金获得者;《福布斯》财富排行榜;世界500强——全球最大500家公司排名等。在个人计算机中最常用的排序就是在【资源管理器】窗口中右键,在弹出的快捷菜单中选择【排列图标】菜单项,按不同的方式排列图标可以很方便的找到想要的文件。以上所讨论的情况都是按照某种规则进行排序,以方便人们查找或检索某一成员。

排序(Sorting)是计算机内经常进行的一种操作,其目的是将一组“无序”的记录序列调整为按关键字“有序”的记录序列。如何进行排序,特别是高效率的排序是计算机工作者学习和研究的重要课题之一。

二.基本概念

关键码(Keyword):作为排序依据的数据项,也称为排序项 关键码分为主关键码(Primary Keyword)和次关键码(Secondary Keyword)。

如果使用某个排序方法对任意的记录序列按关键码进行排序,相同关键码值的记录之间的位置关系与排序前一致,则称此排序方法是稳定的;如果不一致,则称此排序方法是不稳定的。

排序有内排序和外排序之分。

内排序:在排序的整个过程中,不需要访问外存,记录全部在计算机内存中,并且在内存中调整记录之间的相对位置。

外排序:内存无法容纳全部资料,排序需要借助外部存储设备才能完成,在这个过程中,需要不断地在内、外存之间交换数据。

三.总览

排序的算法有很多种,我们主要学习几个典型、常用的内排序。我们会从几个方面分析算法:借助的数据结构+最差时间复杂+最优时间复杂度+平均时间复杂度+所需辅助空间+稳定性

1.直接插入排序

         二分插入排序

2.希尔排序

3.冒泡排序

         鸡尾酒排序

4.快速排序

5.直接选择排序

6.堆排序

7.二路归并排序

四.分类

1.直接插入排序

插入排序(Insertion Sort)的思想是不断地将待排序的元素插入到有序序列中,使有序序列不断扩大,指导所有的元素都被插入到有序序列中。插入排序法有很多种,我们将学习直接插入排序和希尔排序。

直接插入排序是一种简单直观的排序算法。它的工作原理非常类似于我们抓扑克牌。

直接插入排序不断从无序序列中取出插入元素,并把插入元素插入到有序序列的合适位置,直到无序序列的所有元素都被插入到有序序列为止。

算法描述:
1.从第一个元素开始,该元素可以认为已经被排序
2.取出下一个元素,在已经排序的元素序列中从后向前扫描
3.如果该元素(已排序)大于新元素,将该元素移到下一位置
4.重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
5.将新元素插入到该位置后
重复步骤2~5

如图演示(3,6,5,9,7,1,8,2,4)的直接插入排序过程。

数据结构 ---------- 数组
最差时间复杂度 ---- 最坏情况为输入序列是降序排列的,此时时间复杂度O(n^2)
最优时间复杂度 ---- 最好情况为输入序列是升序排列的,此时时间复杂度O(n)
平均时间复杂度 ---- O(n^2)
所需辅助空间 ------ O(1)
稳定性 ------------ 稳定

2.二分插入排序

又称折半插入排序。

当直接插入排序进行到某一趟时,对于前面记录已经按关键字有序,此时不用直接插入排序的方法,而用折半二分查找,找出下一个元素应插入的位置,然后插入,这种方法就是折半插入排序,这种方法中比较次数,由于采用折半查而减少,为O(log2n),但是元素交换的次数仍为O(n2),二分排序算法是稳定的。 二分搜索比顺序搜索查找快,所以二分插入排序就平均性能来说比直接插入排序要快。

算法的基本过程:

(1)计算 0 ~ i-1 的中间点,用 i 索引处的元素与中间值进行比较,如果 i 索引处的元素大,说明要插入的这个元素应该在中间值和刚加入i索引之间,反之,就是在刚开始的位置 到中间值的位置,这样很简单的完成了折半;

(2)在相应的半个范围里面找插入的位置时,不断的用(1)步骤缩小范围,不停的折半,范围依次缩小为 1/2 1/4 1/8 .......快速的确定出第 i 个元素要插在什么地方;

(3)确定位置之后,将整个序列后移,并将元素插入到相应位置。

数据结构 ---------- 数组
最差时间复杂度 ----最坏的情况是当插入的位置不在二分位置 ,时间复杂度是O(n^2)
最优时间复杂度 ----最好的情况是当插入的位置刚好是二分位置 ,所用时间为O(log₂n);
平均时间复杂度 ---- O(n^2)
所需辅助空间 ------ O(1)
稳定性 ------------ 稳定

3.希尔排序

希尔排序(Shell Sort),也叫递减增量排序,是插入排序的一种更高效的改进版本。

是由D.L.Shell在1959年提出的。直接插入排序法适合用于数量较少的排序,当待排序记录序列接近“正序”时,其时间复杂度也可提高至接近O(n).

希尔排序是不稳定的排序算法。

希尔排序是基于插入排序的以下两点性质而提出改进方法的:

①插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率

②但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位 希尔排序通过将比较的全部元素分为几个区域来提升插入排序的性能。这样可以让一个元素可以一次性地朝最终位置前进一大步。然后算法再取越来越小的步长进行排序,算法的最后一步就是普通的插入排序,但是到了这步,需排序的数据几乎是已排好的了(此时插入排序较快)。

假设有一个很小的数据在一个已按升序排好序的数组的末端。如果用复杂度为O(n^2)的排序(冒泡排序或直接插入排序),可能会进行n次的比较和交换才能将该数据移至正确位置。而希尔排序会用较大的步长移动数据,所以小数据只需进行少数比较和交换即可到正确位置。

如图演示(9,1,2,5,7,4,8,6,3,5)希尔排序的过程:

数据结构 ---------- 数组
最差时间复杂度 ---- 根据步长序列的不同而不同。已知最好的为O(n(logn)^2)
最优时间复杂度 ---- O(n)
平均时间复杂度 ---- 根据步长序列的不同而不同。
所需辅助空间 ------ O(1)
稳定性 ------------ 不稳定

4.冒泡排序

冒泡排序是一种极其简单的排序算法,也是我所学的第一个排序算法。

它重复地走访过要排序的元素,依次比较相邻两个元素,如果他们的顺序错误就把他们调换过来,直到没有元素再需要交换,排序完成。

这个算法的名字由来是因为越小(或越大)的元素会经由交换慢慢“浮”到数列的顶端。 冒泡排序算法的运作如下:

①比较相邻的元素,如果前一个比后一个大,就把它们两个调换位置。

②对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。

③针对所有的元素重复以上的步骤,除了最后一个。

④持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

如图是对(10,8,3,15,26,11,30)序列的第一轮比较:

数据结构 ---------- 数组
最差时间复杂度 ---- O(n^2)
最优时间复杂度 ---- 如果能在内部循环第一次运行时,使用一个旗标来表示有无需要交换的可能,可以把最优时间复杂度降低到O(n)
平均时间复杂度 ---- O(n^2)
需辅助空间 ------ O(1)
稳定性 ------------ 稳定

5.鸡尾酒排序

 

数据结构 ---------- 数组
最差时间复杂度 ---- O(n^2)
最优时间复杂度 ---- 如果序列在一开始已经大部分排序过的话,会接近O(n)
平均时间复杂度 ---- O(n^2)
所需辅助空间 ------ O(1)
稳定性 ------------ 稳定

6.快速排序

冒泡排序在扫描的过程中,只对两个相邻的元素进行比较,因此在互换两个元素时候只能消除一个逆序。如果通过两个不相邻的元素的交换能够消除待排序记录中的多个逆序,则会大大加快排序的速度。快速排序(Quick Sort)正是通过不相邻元素交换而消除多个逆序的。

快速排序是C.R.A.Hoare于1962年提出的一种划分交换排序。在各种排序方法中,这种方法对元素进行比较的次数较少,因而速度比较快,被认为是目前最好的排序方法之一。在.net的多个集合类所使用的Sort()方法正是使用快速排序法对集合元素进行排序的。

考虑到排序算法的稳定性。对于基础类型,相同值是无差别的,排序前后相同值的相对位置并不重要,所以选择更为高效的快速排序,尽管它是不稳定的排序算法;而对于非基础类型,排序前后相等实例的相对位置不宜改变,所以选择稳定的归并排序。

快速排序使用分治策略(Divide and Conquer)来把一个序列分为两个子序列。步骤为:

1.从序列中挑出一个元素,作为”基准”(pivot)。

2.把所有比基准值小的元素放在基准前面,所有比基准值大的元素放在基准的后面(相同的数可以到任一边),这个称为分区(partition)操作。

3.对每个分区递归地进行步骤1~2,递归的结束条件是序列的大小是1,这时整体已经被排好序了。

数据结构 --------- 数组
最差时间复杂度 ---- 每次选取的基准都是最大(或最小)的元素,导致每次只划分出了一个分区,需要进行n-1次划分才能结束递归,时间复杂度为O(n^2)
最优时间复杂度 ---- 每次选取的基准都是中位数,这样每次都均匀的划分出两个分区,只需要logn次划分就结束递归,时间复杂度为O(nlogn)
平均时间复杂度 ---- O(nlogn)
所需辅助空间 ------ 主要是递归造成的栈空间的使用(用来保存low和high等局部变量),取决于递归树的度,一般为O(logn),最差为O(n)      
稳定性 ---------- 不稳定

快速排序是目前被认为最好的内部排序方法,但是,如果待排序记录的初始状态有序,则快速排序退化为冒泡排序,时间复杂度O(n^2)。
也就是说待排序记录越乱,基准两侧的数量越相近,排序速度越快。待排序记录越有序,排序速度越慢。
为了避免一趟排序后记录集中在基准的一侧,可以在快速排序前对序列进行“预处理”,将序列的第一个元素,中间元素,最后一个元素进行比较,取中间值作为基准值。

7.直接选择排序

数据结构 ---------- 数组
最差时间复杂度 ---- O(n^2)
最优时间复杂度 ---- O(n^2)
平均时间复杂度 ---- O(n^2)
所需辅助空间 ------ O(1)
稳定性 ------------ 不稳定

8.堆排序

堆排序(Heap Sort)是由J.Williams在1964年提出的,它是在选择排序的基础上发展起来的,比选择排序效率要高。前面讨论的几种排序方法并未过多的涉及其他概念,但堆排序方法除了是一种排序方法外,还涉及方法之外的某些概念:堆和完全二叉树。

若将堆看成一棵完全二叉树,则这颗二叉树中的每个非叶子结点的值均不大于(或不小于)其左、右孩子结点的值。由此可知,若一棵完全二叉树是堆,则根结点一定是这个二叉树的所有节点的最大值或最小值。

非叶子结点的值大于其左、右孩子的值的堆被称为大根堆。

非叶子结点的值小于其左、右孩子的值的堆被称为小根堆。

堆排序的基本思想:将待排序序列构造成一个堆,选出堆中的最大记录,并且在堆中移除,并且将剩余的记录在调整成堆,再找到次大记录。以此类推,直到堆中只有一个记录为止。每个记录的出堆的顺序就是一个有序序列。步骤如下:

1.设堆中的元素个数为n,先取出i= n/2-1(最后一个非叶子节点),将以i结点为根的子树调整成    堆,然后令i=i-1,在将以i结点为根的子树调整成堆。如此反复,直到i=0为止,完成初识堆创建。

2.首先输出堆顶元素,将堆中最后一个元素上移到堆顶位置,这样不会破坏原有的特性,调整根    结点使其继续成为大根堆。

3.重复步骤2,直到输出所有的元素为止。按照输出元素的前后次序排序,就形成了有序序列,从    而完成堆排序操作。

如图演示待排序序列(1,6,5,9,7,1,8,2,4)创建大根堆的过程.

数据结构 ---------- 数组
最差时间复杂度 ---- O(nlogn)
最优时间复杂度 ---- O(nlogn)
平均时间复杂度 ---- O(nlogn)
所需辅助空间 ------ 根据调整堆递归的次数决定
稳定性 ------------ 不稳定

堆排序的执行时间主要由建立初识堆和反复重建堆这两个部分的时间开销构成。堆排序的最坏时间复杂度为
O(nlogn)。他的平均性能接近最坏性能。由于建初始堆所需要比较的次数比较多,所以堆不适用于记录较少的文件。

9.二路归并排序

归并排序(Merging Sort)是利用“归并”技术进行排序的,所谓归并是指将两个或这个以上的有序序列合并成一个新的有序序列。合并是一种常见的运算,其方法就是比较各个子序列的第一个记录,将小者取出作为合并序列的第一个记录,如此继续,最终可以得到一个有序序列。

利用两个有序序列的合并实现归并排序成为二路归并排序。

该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。

分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案合并在一起,即分而治之。

数据结构 ---------- 数组
最差时间复杂度 ---- O(nlogn)
最优时间复杂度 ---- O(nlogn)
平均时间复杂度 ---- O(nlogn)
所需辅助空间 ------ O(n)
稳定性 ------------ 稳定

二路归并排序易于在链表上实现,他的时间复杂度无论是在最好的情况下还是在最坏的情况下均是O(nlogn),
但是二路归并排序与其他排序相比,需要更多的临时空间。

五.排序总结

我们现在学的这些算法都是基于比较的,其实还有一种是非比较排序,时间复杂度可以达到O(n),主要有:计数排序,基数排序,桶排序等。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值