【笔记分享】大话数据结构3:排序
因本人最近在恶补数据结构,学识经验有限,如有不正之处望读者指正,不胜感激;也望借此平台留下学习笔记以温故而知新。这一篇博客主要是最近刚开始接触大话数据结构一书,写的通俗易懂,很多图表帮忙理解,所以讲随手笔记分享至此,希望对您有所帮助。
排序
概念
内排序概念:
内排序是在排序整个过程中,待排序的所有记录全部放置在内存中的排序。
内排序主要收3方面影响:
1、时间性能
2、辅助空间
3、算法的复杂性
按照算法的复杂度分了两大类:
冒泡、简单选择、直接插入
希尔排序、堆排序、归并排序、快速排序
排序用到的数据结构和函数
冒泡排序
一种交换排序,基本思想:两两比较相邻记录的关键字,如果反序则交换,知道没有反序的记录为止
交换排序代码实现1
冒泡排序算法:
冒泡排序优化
说明:根据改进后的代码可以推测最好的情况时没有数据交换,时间复杂度为O(n);
当最坏的情况,此时需要 (n-1)*(n)/2次
简单选择排序
基本思想:通过n-1次关键字间的比较,从n-i+1个记录中选出关键字最下的记录,并和第i个记录进行交换。
比较次数与前述方法雷同,但是每一轮比较只交换数据一次。
尽管时间复杂度与冒泡排序同为O(n2),但是简单选择排序还是要优于冒泡
直接插入排序
直接插入排序的基本操作是将一个记录插入到已经排序好的有序表中,从而得到一个新的、记录数增1的有序表
说明:时间复杂度O(n2),但是性能要好一些
希尔排序
是对插入排序的一种改进
基本思想:将相距某个增量的记录组成一个子序列,保证子序列内分别进行直接插入排序后得到的结果是基本有序而不是局部有序。
说明:希尔排序对于增量的选择很重要,最优选择目前还尚未定论,但是根据经验的话,当增量序列为如下数值,可取的不错效果。
说明:希尔排序的时间复杂度为O(n3/2)
堆排序
是对简单选择排序进行的改进
堆数据结构介绍
堆是具有下列性质的完全二叉树:
每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;
或者每个结点的值都小于或等于其左右孩子节点的值,称为小顶堆
示例
其结点之间满足如下关系
堆排序算法
是利用堆(假设利用大顶堆)进行排序的方法。
基本思想是:将待排序的序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根结点,将其移走(其实就是将其与堆数组的末尾元素交换,此时末尾元素就是最大值),然后将剩余的n-1个序列重新构造成一个堆,这样就会得到n个元素中的次小值。
为了实现堆排序,需要解决两个问题:
1、如何由一个无序序列构建成一个堆?
2、如果在输出堆顶元素后,调整剩余元素称为一个新的堆?
代码实现
堆调整函数
说明:运行时间消耗在初始构建和重建堆的反复筛选上
构建堆的时间复杂度因为对于每个非终端结点最多只需要进行两次比较,所以为O(n)
正式排序时,时间复杂度为O(nlogn)
注意:因为记录的比较和交换是跳跃式进行,所以堆排序是一种不稳定排序方法;此外,因为初始构建堆次数较多,所以不适合排序序列个数较少的情况
归并排序
归并的意思:将两个或两个以上的有序表组合成一个新的有序表
归并排序:
代码实现:
Merge合并函数实现
复杂度分析
一趟归并需要将列表中相邻的长度为h的有序序列进行两两归并,即将序列所有记录扫描一遍,时间复杂度为O(n);而根据完全二叉树的深度计算可以得到归并排序进行logn次;因此总的时间复杂度为O(nlogn);同时,又分别对应到归并排序算法最好、最坏、平均时间性能。
补充:非递归实现归并排序
MergePass代码实现
说明:迭代处理避免了递归时深度为logn的栈空间,空间只是用到了申请归并用的TR数组,因此空间复杂度为O(n)。使用归并排序时,尽量考虑用非递归方法。
快速排序
本质是冒泡排序的升级,都是交换排序类
基本思想:通过一趟排序将待排序的记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小;则可分别对这两部分进行排序。
快排复杂度
最优情况下,pivot每次都是均分一分为二的,则时间复杂度为O(nlogn)
最快情况下,顺序或者逆序的情况,每次划分只比上次多1,最终时间复杂度为O(n2)
平均情况下,时间复杂度为O(nlogn)
快排优化
对于pivot的初始化选择序列第一个数据,放中间值正好不在第一位时,出现潜在的性能瓶颈
改进:三数取中,取三个关键字先进行排序,将中间数作为枢轴,一般是取左端、右端和中间三个数。
在上述Partition函数代码的第3行和第4行之间增加三数取中实现代码:
对于交换代码的优化实现
优化小数组的排序算法
优化递归操作
总结
算法各种指标对比