笔试算法题(53):四种基本排序方法的性能特征(Selection,Insertion,Bubble,Shell)
四种基本算法概述:
基本排序:选择,插入,冒泡,希尔。上述算法适用于小规模文件和特殊文件的排序,并不适合大规模随机排序的文件。前三种算法的执行时间与N2成正比,希尔算法的执行时间与N3/2(或更快)成正比;
前三种算法在平均,最坏情况下都是N2,而且都不需要额外的内存;所以尽管他们的运行时间只相差常数倍,但运行方式不同;
对于已经就序的序列而言,插入排序和冒泡排序的运行时间都是O(N),但是选择排序的时间仍旧是O(N^2);
因为Insertion和Bubble都是相邻项间的比较交换,所以不会出现不稳定因素,为稳定排序;Selection有跳跃交换过程,所以可能出现不稳定情况;
排序算法运行时间的关键因素为:比较次数,数据移动次数;
对于随机序列而言,插入排序不能预见元素在数组中的最终位置;选择排序则不会再接触,改变已排序元素的位置;
对于算法整体情况:Insertion的每一次排序过程中,待插入元素平均需要经过已排序元素的一半,才能找到其插入位置;Selection和Bubble的每一次排序过程中,只需要遍历所有未排序元素并寻找最小项,不同的是随着Bubble排序的进行,未排序部分会接近正序,Selection则仅是一次将最小元素交换到最终位置;
对于已排序或者已经接近正序排列的数组而言,Insertion和Bubble都可以为线性时间完成,但是Selection仍旧为二次时间完成;
对于小型数组而言,Insertion和Selection是Bubble速度的两倍;对于数据移动开销较大的情况,Selection是最优选择;
议题:选择排序(selection sort)
分析:
搜索整个数组,查找最小值元素,并将它与位于数组首位的元素交换;然后在除第一个元素的范围内查找最小值元素,并将它与位于数组次首位的元素交换;重复进行,直到数组最后一个元素终止;
弱势:每一次寻找最小元素的过程中不能利用序列中本身存在的排序信息以及上一次寻找过程的信息,每次查找过程几乎是独立完成,所以对于已排序序列和未排序序列的排序时间相同,为N2;元素之间的比较次数也为N2;
优势:数据移动量,次数达到最小,适合于庞大项,小键值的序列。内循环查找剩余序列中的最大项,外循环每次交换将一个元素放到他的最终位置上(所以总共的交换次数为N-1,在同等级算法中移动次数最少);
性质:不稳定排序, 2.5.8.2.1.6,2会与1进行交换,这时与第二个2的相对顺序改变,大约使用N-1次交换,N(N-1)/2次比较;
时间:算法运行时间与序列状态无关,总是N2。其对输入(也就是数组原有的顺序)不敏感,为适应性排序;
样例:
void selectSort(int *array, int l, int r) {
int minimum;
int temp; /**
* 外循环从左向右遍历处理array的每一个元素,
* 由于到达array[r]的时候,其左边的元素已经就序
* 所以不用处理array[r];
* */
for(int i=l;i<r;i++) {
/**
* 使用minimum记录array中最小元素的索引
* */
minimum=i;
for(int j=i+;j<=r;j++) {
if(array[minimum]>array[j])
/**
* 发现更小元素时仅更新索引
* */
minimum=j;
}
/**
* 由于选择排序并不是相邻元素之间的交换,所以
* 可能破坏数据的稳定性,选择排序是非稳定性算法
* 如:2,5,8,2,1,6序列中,第一个2就会因为与1交换,
* 从而排在第二个2的后面
* */
temp=array[minimum];
array[minimum]=array[i];
array[i]=temp;
}
} int main() {
int array[]={,,,,,};
selectSort(array,,);
for(int i=;i<;i++)
printf("%d,",array[i]);
}
议题:插入排序(insertion sort)
分析:
实现之一:假定位于当前索引项左边的序列已经排好序,将当前项插入到左边序列中的合适位置,定位方法是从右向左依次比较,当遇到小于等于自己的项时停止,循环进行,直到数组结束;
实现之二:主要是对实现之一的改进。针对点有二:避免无谓的赋值;减少测试条件。实现之一的内循环有两个检测条件,数组下标是否小于0和数组元素是否小于 等于当前元素,由于第一个条件在很少情况下才成立,所以可想办法简化。方法是:首先将数组中的最小值放于数组开始处,然后在剩余的范围中进行插入排序,从 而内循环仅有一个检测条件;
弱势:运行效率取决于待排序序列的已排序程度(非适应性算法),顺序序列运行时间为N,逆序序列运行时间为N2。并且一次数据移动并不能保证此数据项在正确的排序位置,还需要为后面更小的元素移动;
优势:减少数据复制,减少测试条件,使用标记元素将内循环中的多个条件测试合并为一个,目标元素在一次插入过程中仅有一次移动(虽然不是最终位置,虽然需要平移其他元素);
性质:稳定排序,内部循环的跳出条件之一是判断数组元素是否小于当前索引元素,是的话就将当前索引元素插入其右边,所以相同键值的元素顺序不变。N2 /4次比较,N2 /4次半交换(也就是中间元素的顺移),最坏情况时比较和交换次数都加倍;
时间:序列状态影响运行效率,顺序序列为N,逆序序列为N2,平均为N2;
样例:
void insertSort_1(int *array, int l, int r) {
int temp;
int index; /**
* 外循环从左向右的第二个元素开始处理array的
* 每一个元素; 并假定当前索引i左边的元素序列
* 都已经就序;
* */
for(int i=l+;i<=r;i++) {
/**
* 插入排序的策略是将array[i]的值插入到
* i左边已经就序的序列中;
* */
temp=array[i];
index=i-;
/**
* 第一个循环条件保证在到达array最左边时停止
* 第二个循环条件保证仅当temp更小时才继续处理
* 第二个循环条件保证插入排序是稳定性
* */
while(index>=l && array[index]>temp) {
/**
* 交换方式时将左边的元素顺次复制到紧靠
* 其右边的元素
* */
array[index+]=array[index];
index--;
}
/**
* 最终将array[i]放到指定位置
* */
array[index+]=temp;
}
} void insertSort_2(int *array, int l, int r) {
int temp;
int index;
int minimum=l; /**
* 寻找array中最小值,并将其与array[l]的值进行交换
* */
for(int i=l+;i<=r;i++) {
if(array[minimum]>array[i])
minimum=i;
}
temp=array[minimum];
array[minimum]=array[l];
array[l]=temp;
/**
* 外循环从左向右的第二个元素开始处理array的
* 每一个元素; 并假定当前索引i左边的元素序列
* 都已经就序;
* */
for(int i=l+;i<=r;i++) {
/**
* 插入排序的策略是将array[i]的值插入到
* i左边已经就序的序列中;
* */
temp=array[i];
index=i-;
/**
* 由于array[l]的元素为array中最小的元素
* 所以,内循环只需要一个循环条件,从而减少
* 内部循环的代码,加快执行速度
* */
while(array[index]>temp) {
/**
* 交换方式时将左边的元素顺次复制到紧靠
* 其右边的元素
* */
array[index+]=array[index];
index--;
}
/**
* 最终将array[i]放到指定位置
* */
array[index+]=temp;
}
} int main() {
int array[]={,,,,,};
insertSort_2(array,,);
for(int i=;i<;i++)
printf("%d,",array[i]);
return ;
}
议题:冒泡排序(Bubble Sort)
分析:
实现之一:从左向右遍历数组,此为外循环,每一次遍历中将当前元素与它右边紧接的元素比较,较大的放于右边,重复这样的操作直到数组末尾,此为内循环,结束之后然后进入下一次外循环;非适应性算法,无论给定数据序列状态如何,运行时间相同;
实现之二:当数组是顺序时,内层循环的每一次并不会进行交换;所以检测当且仅当内层循环一遍之后,没有发生任何交换,则可以跳出外层循环,此时排序完成。适应性算法,根据初始数据序列的状态决定排序时间;
弱势:需要多次移动数据(平均每两次比较会又一次移动);一般来说,冒泡排序比其他两种N2排序算法要慢;
优势:易于实现,前面的操作会为后面的操作提供排序信息提升性能(较大元素往右边集中),一次遍历将一个元素放到最终位置。可以改进为摇摆排序(Shaker Sort,将单向扫描数组改成从头到尾,从尾到头的交替式移动);
性质:稳定排序,比较过程中仅当左边元素大于右边元素才交换,同等情况下比选择,插入排序慢。最坏情况下平均要N(N-1)/2次比较,N(N-1)/2次交换;
时间:序列状态影响运行效率,顺序序列为N,逆序序列为N2,平均为N2;
样例:
void bubbleSort_1(int *array, int l, int r) {
int temp; /**
* 外循环从左向右遍历,每一次针对array上的一个位置i
* 的值;由于每次都是讲最小的元素交换到i的位置,所以
* 当处理array[r]的时候,最小的r-l个元素已经就位,
* 所以不用处理r位置的元素
* */
for(int i=l;i<r;i++) {
for(int j=r;j>i;j--) {
if(array[j-]>array[j]) {
/**
* 内循环从右向左处理每两个元素,并将
* 较小的元素放置到左边的位置
* */
array[j-]=array[j-]^array[j];
array[j]=array[j-]^array[j];
array[j-]=array[j-]^array[j];
}
}
}
} void bubbleSort_2(int *array, int l, int r) {
int temp;
bool isStable; /**
* 外循环从左向右遍历,每一次针对array上的一个位置i
* 的值;由于每次都是讲最小的元素交换到i的位置,所以
* 当处理array[r]的时候,最小的r-l个元素已经就位,
* 所以不用处理r位置的元素
* */
for(int i=l;i<r;i++) {
/**
* 加入isStable标志,如果内循环没有发生任何交换
* 操作,则说明序列已经就序,则直接跳出外循环;
* */
isStable=true;
for(int j=r;j>i;j--) {
if(array[j-]>array[j]) {
/**
* 内循环从右向左处理每两个元素,并将
* 较小的元素放置到左边的位置
* */
array[j-]=array[j-]^array[j];
array[j]=array[j-]^array[j];
array[j-]=array[j-]^array[j];
isStable=false;
}
}
if(isStable)
break;
}
} int main() {
int array[]={,,,,,};
bubbleSort_2(array,,);
for(int i=;i<;i++)
printf("%d,",array[i]);
return ;
}
议题:希尔排序(增量式插入排序)(Shell Sort)
分析:
对插入排序的改进,插入排序中元素仅能通过与相邻元素的交换一步一步形成有序序列。现在首先大粒度地将元素交换到属于他的区域,再小粒度进一步缩小区域, 最终到达正确位置,这样可以减少中间不必要的交换,所以使得序列在一开始就整体上向着有序的方向前进,而进行远距离间隔的交换能达到目的;
希尔排序使用一 个递减的间隔序列,分批进行插入排序,也就是间隔N时,分别对序列中间隔N的元素进行插入排序,然后对间隔next(N)的元素进行插入排序,最终间隔序 列达到1(也就是常规的插入排序);
弱势:增量序列(Incremental Sequence)很难选择;
优势:不像插入排序那样一次交换仅仅涉及邻接项,希尔排序是首先就在整体上使得序列趋于有序,然后逐渐细致到每项(这个时候序列已经呈现得很有序);
性质:不稳定排序,相同索引键项在不同的间隔序列中可能在各自序列中被交换;使用的增量序列为:1,4,13,40,121,…………构建函数为 h=3*h+1,获取函数是h=h/3。在函数最开始的时候,根据数组元素的个数调整h的最大值,限制函数h<=(r-1)/9。使用增量三角(确 定当前值X后,他的左上方的值为x/3,右上方的值为x/2。前三项为1,2,3,然后按照规则生成的三角数阵,此为Pratt序列)获得的序列可以使得 比较次数少于N(㏒2 N)2;
时间:使用Knuth序列为N3/2,使用增量三角序列(Pratt序列)为N(㏒2 N)2;
样例:
void shellSort(int *array, int l, int r) {
int temp;
int index;
int interval;
/**
* 构造增量序列:1,4,13,40,121,364,……
* 此序列由Knuth在1969年推荐的;
* 为了保证对于一个增量interval而言,序列array中
* 都至少有三个元素l,l+h和l+2h,则需要加入(r-l)/9的限制
* 此序列最多可将shellsort算法的速度提升25%
*
* 当然也可以使用序列:1,8,23,77,281,1073,……
* */
for(interval=;interval<=(r-l)/;interval=*interval+);
/**
* 确定增量序列的最大可用值之后,按照从大到小的顺序对array序列
* 进行interval-增量排序,interval/=3可以取得下一个增量元素
* */
for(;interval>;interval/=) {
/**
* 内部循环完全是插入排序,只是之前处理相差1的元素需要替换为
* 相差interval的元素
* */
for(int i=l+interval;i<=r;i++) {
temp=array[i];
index=i-interval; while(index>=l && array[index]>temp) {
array[index+interval]=array[index];
index-=interval;
} array[index+interval]=temp;
}
}
} int main() {
int array[]={,,,,,};
shellSort(array,,);
for(int i=;i<;i++)
printf("%d,",array[i]);
return ;
}
笔试算法题(53):四种基本排序方法的性能特征(Selection,Insertion,Bubble,Shell)的更多相关文章
- 笔试算法题(58):二分查找树性能分析(Binary Search Tree Performance Analysis)
议题:二分查找树性能分析(Binary Search Tree Performance Analysis) 分析: 二叉搜索树(Binary Search Tree,BST)是一颗典型的二叉树,同时任 ...
- php四种基础排序算法的运行时间比较
/** * php四种基础排序算法的运行时间比较 * @authors Jesse (jesse152@163.com) * @date 2016-08-11 07:12:14 */ //冒泡排序法 ...
- PHP四种基本排序算法
PHP的四种基本排序算法为:冒泡排序.插入排序.选择排序和快速排序. 下面是我整理出来的算法代码: 1. 冒泡排序: 思路:对数组进行多轮冒泡,每一轮对数组中的元素两两比较,调整位置,冒出一个最大的数 ...
- php四种基础排序算法的运行时间比较!
/** * php四种基础排序算法的运行时间比较 * @authors Jesse (jesse152@163.com) * @date 2016-08-11 07:12:14 */ //冒泡排序法 ...
- 四种简单的图像显著性区域特征提取方法-----AC/HC/LC/FT。
四种简单的图像显著性区域特征提取方法-----> AC/HC/LC/FT. 分类: 图像处理 2014-08-03 12:40 4088人阅读 评论(4) 收藏 举报 salient regio ...
- iOS中常用的四种数据持久化方法简介
iOS中常用的四种数据持久化方法简介 iOS中的数据持久化方式,基本上有以下四种:属性列表.对象归档.SQLite3和Core Data 1.属性列表涉及到的主要类:NSUserDefaults,一般 ...
- WordPress忘记密码找回登录密码的四种行之有效的方法
WordPress忘记密码找回登录密码的四种行之有效的方法 PS:20170214更新,感谢SuperDoge同学提供的方法,登入phpMyAdmin后,先从左边选自己的数据库,然后点上面的 SQL ...
- Scrapy里Selectors 四种基础的方法
在Scrapy里面,Selectors 有四种基础的方法xpath():返回一系列的selectors,每一个select表示一个xpath参数表达式选择的节点css():返回一系列的selector ...
- 笔试算法题(54):快速排序实现之单向扫描、双向扫描(single-direction scanning, bidirectional scanning of Quick Sort)
议题:快速排序实现之一(单向遍历) 分析: 算法原理:主要由两部分组成,一部分是递归部分QuickSort,它将调用partition进行划分,并取得划分元素P,然后分别对P之前的部分和P 之后的部分 ...
随机推荐
- 解决axios IE11 Promise对象未定义
在你的项目中安装polyfill Babel Polyfill 按照官网方法安装并引入即可 http://blog.csdn.net/panyox/article/details/76377248
- bzoj 3206: [Apio2013]道路费用【最小生成树+并查集】
参考:http://hzwer.com/6888.html 把k条道路权值设为0,和其他边一起跑MST,然后把此时选中的其他边设为必选,在新图中加上必选变缩成k个点,把所有边重标号,枚举k跳边的选取情 ...
- centos6.5 系统-搭建lamp(php7)环境--(yum在线安装)
1.查看当前服务器的版本 #方法1 cat /etc/redhat-release #方法2 rpm -q centos-release 2.安装apache #一键安装apache yum inst ...
- 安卓小程序的一次bug调试,报错:java.lang.NullPointerException,logcat学习
做实验的时候,调试了很久后模拟器执行后,app还是会崩溃并停止运行,错误如下. 因为初学,所以也不知道怎么使用调试工具,也不懂看日志,经过学习后尝试这查看了LogCat日志上面有这样的提示: 其中引起 ...
- 如何管理第三方接口token过期时间
背景: 随着微服务的盛行,做开发时不可避免的要涉及第三方接口,安全起见,这些接口都会需要一个token参数.而token一般都会有一个过期时间,比如2小时或者30分钟.那么如何在自己的应用中存储并管理 ...
- _bzoj3223 Tyvj 1729 文艺平衡树【Splay】
传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=3223 裸的,打个标记. #include <cstdio> #include & ...
- magento getMessage 不显示或者显示html标签解决方案
在模板页面不显示getMessage的解决方案是,在对应的控制器里加上如下代码: $this->_initLayoutMessages('customer/session'); 如果加入后出现如 ...
- [转]Entity Framework and SQL Azure
本文转自:https://msdn.microsoft.com/zh-cn/library/gg190738 Julie Lerman http://thedatafarm.com April 201 ...
- Oracle的一些名词和概念
1.数据库 这里的数据库不是通常情况下我们所说的数据库,而是一个Oracle的专业名词.它是磁盘上存储数据的集合,在物理上表现为数据文件. 日志文件和控制文件等,在逻辑上以表空间形式存在.使用时,必须 ...
- Glide清除缓存
Glide是谷歌推荐的一款加载图片的第三方框架,对内存优化更好,更省资源,他的众多好处,我就不一一描述了,有兴趣的朋友可以百度一下,介绍的还是挺详细的. 今天主要给大家介绍一下关于怎么获取Glide的 ...