排序和查找-线性排序算法和查找特定值
来源:互联网 发布:行知中学老师 编辑:程序博客网 时间:2024/05/16 16:22
一、排序算法的时间下界
常用排序算法中的冒泡排序、选择排序、插入排序等排序算法都是基于比较的。对于基于比较的排序算法,其时间下界为Ω(nlgn).用决策树模型可以证明该结论。基于比较的排序算法的每一次排序都可以看做是一次决策,决策的结果是比较的两个值是大于或者小于等于。考虑三个元素的排序,以下标表示每个元素,我们可以构造如下的决策树:
图中的每个非页节点表示要比较的两个元素的下标,每个节点的左子树表示比较的两个元素中的前一个小于等于后一个,右子树表示被比较的两个元素的前一个大于后一个。每个叶子节点表示一个可能的排序结果。很显然根据排列组合的原理,三个元素有3!种排列,因此该决策树有3!个叶子。这也可以从另一方面进行解释:排序算法是针对的任意输入组合,因此所有的3!种排列形式都可能是最终的排序结果,那么为了得到一个由任意的输入的三个元素的排序结构,一个正确的排序算法也就必须可以得到这3!种排列中的任意一种,当我们使用基于比较的排序算法时,其对应的决策树就必须有3!个叶子。很显然对于n个节点的排序,该结论也成立。
在得到决策树之后,基于比较的排序算法的时间下界就很明显了,它就是决策树中根节点到叶子节点的最长路径,也就是决策树的高度。假设对n个节点进行排序的决策树的高度为h,由于决策树是一棵二叉树,因而可知树的最大叶子数目为2^h,同时根据对决策树的分析,决策树的叶子数目为n!,因而可得n!<= 2^h。对该算是取对数即可得h>=lg(n!)。又因为lg(n!)=Ω(nlgn),因而h>= Ω(nlgn)。所以h的最小值为Ω(nlgn),因此基于比较的排序算法的时间下界即最好情况为Ω(nlgn)。
二、线性排序算法
除了常用的基于比较的排序算法之外,还有一些非基于比较的排序算法,这些排序算法的排序效率不受Ω(nlgn)的限制。这些排序算法有:计数排序,基数排序,桶排序。
1.计数排序
1).基本概念
计数排序假设n个输入中的每一个元素都是介于0到k之间的整数,k为一个确定的有限值。其基本思想是对每一个输入元素x,确定小于x的元素个数,有了这个信息,就可以把元素x直接放到它在排序结果的位置上。其算法步骤(输入序列在数组A中,辅助数组C,结果存放于数组B中):- 辅助数组C的所有元素清0,辅助数组长度为k,这也是为什么k要为一个确定的有限值的原因。
- 遍历待排序元素序列,对于序列中任意一个元素x,执行:C[x]=C[x] + 1。这一步的操作保证了辅助数组中下标为x的元素的值等于待排序序列中元素x的数目
- 遍历辅助数组C,执行C[x]=C[x-1] + 1。这一步保证了辅助数组中的下标为x的元素的值等于待排序序列中小于等于x的元素的数目。
- 以j为循环变量,从输入数组的最后一个下标开始遍历输入数组,并对每一步执行
- B[C[A[j]]] = A[j],这一步保证了该算法的稳定性,当同一个元素出现多次时,它们的相对位置不变。
- C[A[j]] = C[A[j]] - 1
2)性能
- 时间复杂度:很显然该算法只需要几次简单的循环遍历数组的操作,其时间复杂度为 Θ(n + k)。
- 空间复杂度:该算法需要两个辅助数组,一个长度为k一个长度为待排序序列的长度
- 稳定性:该算法是稳定的
2.基数排序
1)基本思想
基数排序的思想是对于一个由n个d位数组成的待排序序列,首先将它们按照最低有效位的数字进行排序,然后按照次有效位排序。。。最后按照最高有效位排序。这种有效位排序次序可以保证在按照一个有效位进行排序时,只要两者大小不同就绝对可以按照它们的相对大小进行排序,如果使用最高有效位、次高有效位。。。最低有效位的次序,则是不能简单按照本次比较结果来排序的,比如91和19,按最高有效位排成了19,91,再按照最低有效位排序时是不能排成91,19的。
基数排序中另外一个需要注意的是:按照任意一个有效位排序时,排序必须是稳定的,这是因为在按照该位排序前,可能已经按照其它位将序列排序了,该位上的两个值在其它位上是有序的而且已经排好了,如果不稳定就会破坏以前的排序结果。
基数排序的算法很简单,就是依次按照各个有效位进行排序,只要保证各个有效位上的排序是稳定的,则完成各个有效位上的排序后,序列就是有序的了。举例如下:
待排序序列 最低位排序结果 次第位排序结果 最高位排序结果
008 761 008 008
187 842 235 187
235 235 842 235
657 765 657 657
761 187 761 761
842 657 765 765
765 008 187 842
2)性能
- 时间复杂度:对于n个d位数,每一个数位可以取k个不同的值,如果每个有效位上的稳定排序的时间复杂度为 Θ(n + k),则基数排序的时间复杂度为Θ(d(n + k))。进一步的,可以对其进行扩展,对于给定的n个b(这里b为二进制的位数)位数和任何正整数r<=b,基数排序可以在Θ((b/r)(n + 2^r))的时间内完成排序d/r就相当于d,2^r相当于k。
- 空间复杂度:其空间复杂度取决于所使用的稳定排序的算法的空间复杂度。
- 稳定性:根据该算法的要求可知其是稳定的。
3.桶排序
1).基本思想
2).性能
- 时间复杂度:桶排序的时间复杂度为映射时间复杂度加上各个桶排序的事件复杂度,假设各个桶采取插入排序,则为Θ(n) + O(n0^2) + O(n1^2)+ ... + O(n(n-1)^2),其中n0,n1,n(n-1)为各个桶中元素的数目。当各个桶尺寸的平方和与总的元素数呈线性关系时,根据该式子同排序都可以以线性时间运行。
- 空间复杂度:桶数目加上各个桶进行排序所需要的空间。
- 稳定性:取决于各个桶所采用的排序算法的稳定性。
三、查找特定值
1.最小值和最大值
查找给定序列中的最大值和最小值是经常被碰到的问题,很明显对于任意一个包含n个元素的序列来说,只要经过n-1次比较久可以确定其最大值或者最小值。如果要同时找到最大值和最小值则所需时间为3 * (n/2)向下取整,方法是:
- 如果序列长度n为奇数,则首先让当前的最大值和最小值都等于第一个元素;如果序列长度n为偶数,则首先比较前两个元素,然后取二者中的较小的为当前的最小值另一个为当前的最大值
- 每次取两个元素,向让两个元素相互比较,然后取其中的较小值和当前最小值比较,另一个和当前的最大值比较,并根据结果更新当前的最大、最小值
- 重复第2步,直到序列检查完毕
2.查找任意给定序列中的特定的值
对于任意给定的输入序列,可以在Θ(n)的时间内找到第i小的元素。算法的思想是采用快速排序的方法,但是区别于快速排序的是快速排序在将序列分为三部分后(小于选择的元素的部分,选择的元素,大于选择的元素的部分),会对小于所选择的元素的部分以及大于所选择的元素的部分递归的进行处理,但是在这里只需要根据所选择的元素的位置处理其中的一部分即可。算法思路(假设查找数组array[m..n]中第i小的元素):
- 如果m=n,则返回array[m]
- 将数组array划分为三部分,并返回所选择的元素的下标s
- 让q=s-m+1
- 如果q=i,则发挥array[q]
- 如果i<q,则递归的用该算法在数组array[m, s-1]中查找第i小的元素
- 否则,则递归的用该算法在数组array[s+1, n]中查找第i-q小的元素
3. 有序序列中的查找
- 从序列中找出一个元素,假设为x,则它将序列未被查找过的部分分为三部分:在x之前的部分,假设为A;x;以及在x之后的部分,假设为B(A和B都可能为空,A和B都为空表示序列已经只剩一个元素需要检查了,此时查找必然要结束,要么成功要么失败)。
- 将要查找的元素和x比较,如果相等则查找结束,否则根据其与x的大小关系来在A或者B中查找(x以及未被选中的部分都可以认为是已经查找过了。
- 令pi =(wi+1 +wi+2 + … + wn) – (w1 + w2 + ... +wi-1)
- 选出使得pi最小的元素i当做要构造的二叉树的根
- 分别对在第i个元素之前的子序列和在第i个元素之后的子序列同样构造次优二叉树
- 令Wi=w1 + w2 +... + wi-1 + wi
- 则pi = (wi+1+ wi+2 + … + wn)– (w1 + w2 + ... +wi-1) = Wn - Wi - Wi-1
四、外部排序
当待排序的序列很大,大的无法同时将它们存入内存中进行排序时,就需要进行特别的处理了。这样的待排序序列一般存储在文件中,即存储在外部存储器中,因而这种排序方法被称为外部排序。
1.外部排序方法
由于无法将数据全部加在到内存,因此很自然的就想到了分治法,基本思想是:
- 将待排序序列划分为m个小的序列
- 对每个小序列进行排序,并将结果存储到文件中
- 对得到的m个已经排好序的文件的内容进行合并,并将结果存储到结果文件中
初始内部排序所需时间+每趟归并所需时间 * 归并趟数+归并趟数*一次归并IO操作所需时间
很显然:
- 初始内部排序所需时间:取决于所采用的排序算法,算法确定,它就是确定的
- 每趟归并所需时间* 归并趟数:每一趟归并操作就是一个内存中的比较合并操作,因此该项所需时间也是确定的,因此该项的时间为归并趟数乘以每趟归并所需时间
- 归并趟数*一次IO操作所需时间:首先,为什么这一项会是这种表达方式,这是因为每一趟合并都需要将所有的数据多读入内存,然后处理完后再写入文件,而IO操作都是以块为单位的,因而在总数据量不变的情况下,每一趟所需的时间大致是相等的(之所以是大致相等是因为在归并中,有时候可能会出现落单的有序段,它就不必读到内存再写入文件了,因此是大致相等。比如如果有m=9,采用2路归并,则第一趟中,第9个小段就不需要读入内存再写入文件)
k路归并指的是将k个序列合并成一个新的序列(在k路归并中,如果一组归并可以不够k路,比如对5个序列进行二路归并,则就是将1和2进行归并,3和4进行归并,5自己进行”归并“)。
2.多路归并
- 从每一路中取出其最小(大)值
- 将这个k个值进行比较,找出其中的最小(大)值
- 将得到的元素放入结果序列
在这一步中上一步的胜利者继续进行比较,“失败者”98被记录到其父节点。胜利者101即是要选择的元素,显然只经过了3次比较(log27向上取整)就得到了结果。
这就是败者树的原理,当利用它进行k路归并时,如果某一路的当前元素在树中胜出,就从该路取下一个元素继续参与树的比较即可。这就是用败者树进行k路的归并的方法。
3.置换-选择排序
从上边的描述也可以看出,降低外部排序的另一个方法是减小m,那就要增大每个子序列的长度,但是一个子序列长度选多大才是最合适的是很难确定的。为此引入了置换-选择排序,它的特点是在得到初始归并段的过程中,选择最小(大)值的过程和IO输入、输出同时进行。
假设待排序的序列存放在文件input中,初始归并序列都输出到文件output中,内存中一次可以容纳m个元素,其算法过程如下:
- 从input文件中读取m个元素到内存中。
- 利用败者树从m个元素中选取一个当前最小的元素,并且将其记录到MIN中。
- 将值等于MIN的元素输出到文件output中。
- 如果input不空,则从input中读取下一个元素到内存中。
- 从内存中的所有关键字比MIN大的元素中选出具有最小关键字的元素,并且将MIN更新为该值。这一步将保证输出到文件中的每个序列中的元素都是递增的。
- 重复步骤3-5,直到选不出新的MIN元素为止,此时就得到一个初始归并序列,输出一个归并段结束标记到文件output中
- 重复步骤2-6,直到内存中没有任何元素
由于对于任意一个元素只要参与一趟归并就需要进行一次IO的读写操作,因此当各个序列的长度不同时,我们期望长度越长的序列参与归并的趟数越少,这样我们就可以降低总体的磁盘IO次数,显然赫夫曼树可以用于这个目的(用序列长度当做叶子节点权重来构造一颗具有最小带权路径长度的树)。
- 排序和查找-线性排序算法和查找特定值
- 查找和排序算法
- 算法--排序和查找
- 排序和查找算法
- 排序算法和查找算法
- 排序算法和查找算法
- 排序算法和查找算法
- 排序算法和查找算法
- 查找算法和排序算法
- 几个排序和查找算法
- 妙趣横生算法-查找和排序
- 排序和查找算法总结
- 排序和查找算法总结
- 二分查找和排序算法
- 排序和查找算法--C
- 各种排序算法和查找算法
- 数据结构中排序和查找的算法
- Java排序算法和二分查找
- windows下Qt Creator 调试器配置经历
- The absolute uri: http://java.sun.com/jsp/jstl/core cannot be resolved in either web.xml or the jar
- Windbg调试命令详解(2)
- Lsof Readme and Quick Start for Lsof(1,2,3)
- 设备驱动-----down_interruptible函数
- 排序和查找-线性排序算法和查找特定值
- Best Time to Buy and Sell Stock leetcode
- 初学linux操作系统——问问“男人”
- 3-4 数字三角形问题
- java模式总结1
- 一天一天学 windows phone 引言 + 大概认知 之 保存恢复页面状态(八)
- 算术表达式树
- 一个数据包的生命历程
- HDU 4009 Transfer water (最小树形图,建图,4级)