算法的稳定性:若是待排序的两个元素Ri,Rj,其对应的关键字keyi=keyj,且在排序前Ri在Rj的前面,若是排序后Ri还在Rj的前面,则称这种排序算法是稳定的,不然称排序算法是不稳定的。node
内部排序和外部排序:内部排序是指在排序期间,元素所有存放在内存中的排序。外部排序是指排序期间元素没法所有同时存放在内存中,必须在排序过程当中根据要求不断地在内外存之间移动的排序。算法
1.插入排序数组
1)插入排序:每次将一个待排序的记录,按其关键字大小插入到前面已经排好序的子序列中,直到所有记录插入完成。app
时间复杂度:O(n2),空间复杂度:O(1).稳定性:稳定的排序方法。函数
2)希尔排序:将待排序表分割成若个形如L[i,i+d,i+2d,i+3d,.....i+kd]的特殊子表,分别进行直接插入排序。当整个表呈基本有序时,在对全体记录进行一次直接插入排序。性能
过程:先去一个小于n的步长d1,把表中所有记录分红d1个组,全部距离为d1的倍数的记录放在同一组中,在各组中进行直接插入排序。而后取第二个步长d2<d1.重复上述过程,直到di=1,即全部记录在同一组中,再进行直接插入排序。测试
增量求法:目前不统一,通常采用d1=n/2,,ui
时间复杂度:当n在某个特定范围时为spa
不稳定排序3d
2.交换排序
冒泡排序:将设待排序表长为n,从后往前两两比较相邻元素的值,若为逆序,则交换他们,直到序列比较完。此为一趟冒泡。结果为将最小的元素交换到待排序的第一个位置。下一趟冒泡时,前一趟肯定的最小元素再也不参与比较,待排序列减小一个元素,每趟排序吧最小元素放到最终位置,这样最多作n-1趟冒泡就把全部元素排好。
过程:首先假定划分算法已知,记为partition(),返回上述中的k,L(k)已经在最终位置上,因此能够先对表进行划分,然后对表调用一样的排序操做。递归的调用快速排序算法进行排序。程序结构以下:
1)两个下标分别从首,尾向中间扫描的方法
假设每次都是以当前表中第一个元素做为枢纽值对表进行划分,则必须将表中比枢纽值大的元素向右移动,比枢纽值小的元素向左移动,使得一趟partition()操做后,表的元素被枢纽值一分为二。
若初始序列3,8,7,1,2,5,6,4排序过程以下:
2 8 7 1 2 5 6 4
2 8 7 1 8 5 6 4
2 1 7 1 8 5 6 4
2 1 7 7 8 5 6 4
2 1 3 7 8 5 6 4 //A[high]A[low]
2)两个指针索引一前一后逐步向后扫描
快速排序是全部内部排序算法中平均性能最优的排序算法。在快速排序算法中,并不产生有序子序列,但每一趟排序后将一个元素(基准元素)放在其最终位置上。当初始排序表基本有序或基本逆序是,就获得最坏状况下的时间复杂度O(n2).
A快排一次排序的应用
A)区分数组中大小写字母(编写函数,让小写字母在全部大写字母以前)
b)给定含n个元素的整型数组a,包含0和非0,对数组进行排序,使排序后知足1.排序后的全部0元素在前,非零元素在后,且非零元素排序先后相对位置不变,不能使用额外的存储空间。
c)荷兰国旗问题
D)输入n个整数,输出其中最小的k个。
思路1:将输入的n个数排序,这样排在最前面的k个数就是最小的k个数。
思路2:假设最小的k个数中最大的为A。在快排中,先在数组中随机选择一个数字,而后调整数组中数字的顺序,使得比选中数字小的数字排在他的左边,比选中数字大的排在他的右边(快排一次)
若选中的数字下表恰好是k-1(从0开始),那么这个数字(A)加上左侧的k-1个数就是最小的k个数。若是他的小标大于k-1,则A位于他的左侧,咱们能够在他的左边部分的数组中查找。若小标小于k-1,那么A应该位于他的右边,咱们能够接着在他的右边部分中寻找。(发现这是一个递归问题,可是咱们找到的k个数不必定是有序的)
3.选择排序
思想:每一趟在后面n-i+1(i=1,2..n-1)个待排序元素中选取关键字最小的元素,做为有序子序列的第i个元素,直到n-1趟作完,待排序元素只剩下1就不用再选了。
1)简单选择排序
空间复杂度:O(1)。时间复杂度:元素移动较少不超过3(n-1)(一次swap三次元素移动)。最好移动0次(此时表已经有序)。可是元素间比较的次数与序列的初始状态无关,始终为n(n-1)/2次。时间复杂度为O(n2).
2)堆排序
堆排序是一种树形选择排序方法,在排序过程当中将L[1..n]视为一棵彻底二叉树的顺序村粗结构。利用彻底二叉树中双亲结点和孩子结点之间的内在关系,在当前无序区中选择关键字最大(或最小)的元素。
堆排序的实质是构建初始堆,对初始序列建堆,就是一个反复筛选的过程。
A)根据初始关键字序列(20,18,22,16,30,19)构建初始大根堆。
在元素个数为n的序列上建堆,其时间复杂度为O(n),这说明能够在线性时间内,将一个无序数组建成一个大顶堆。
B)堆排序的思想
因为堆自己的特色(以大顶堆为例),堆顶元素就是最大值。输出堆顶元素后,一般将堆底元素放入堆顶,此时根节点已不知足堆的性质,将堆顶元素向下调整继续保持大顶堆性质,输出堆顶元素,重复,直到仅剩一个元素为止。
C)堆的插入和删除
删除堆顶元素时,先将堆的最后一个元素与堆顶元素交换,有序性质破坏,须要堆根结点进行向下调整。
对堆进行插入操做时,先将新结点放在堆的末端,再对这个新结点执行向上调整操做,大顶堆插入操做以下图所示:
向上调整算法以下所示:
D)堆排序的应用(最小k个数)
输入n个整数,输出其中最小的k个.(用堆排序来解决,适合处理海量数据)
思路:首先读入k个数建立一个大小为k的大顶堆,而后依此读入剩余数据,若是当前数据比大顶堆的堆顶小,则用这个数代替当前堆顶元素,并调整时期保持大顶堆性质,若是当前数据比堆顶大,则此数不可能为最小的k个整数之一,故抛弃此数。(时间复杂度:O(nlogk))
4.归并排序
含义:将两个或两个以上的有序表组合成一个新的有序表。假定待排序表含有n个记录,则可视为n个有序子表,每一个子表长度为1,两两归并,获得
过程:分解:将n个元素的待排序表分红各含n/2个元素的子表,采用二路归并算法对两个子表递归的进行排序。
合并:合并两个已排序的子表获得排序结果。
Merge()的功能时将先后相邻的两个有序表归并为一个有序表的算法。设两段有序表A【low...mid】A[mid+1...high]存放在同一顺序表中相邻的位置上,先将他们复制到辅助数组B中,每次从对应B中的两个段取出一个记录进行关键字比较,将较小者放入A中,当输入B中有一段超出其表长,则将另外一段剩余部分直接复制到A中。
A)合并两个排好序的链表(连个递增排序链表,合并他们使新链表结点仍然是按照递增排序的)
b)给定有序数组a,b.已知数组a末尾有足够空间容纳b,请实现将b合并到a中。函数头以下:
Void merge(int a[],int b[],int n,int m)//n为数组a的元素个数,m为数组b的元素个数
思路:先计算总元素个数,从数组末尾(最大元素)开始归并。
C)原地归并排序(二叉归并排序 内部排序,不适用辅助空间)
原地归并排序不须要辅助数组便可归并。关键在merge这个函数。假设有两段递增的子数组arr[begin....mid-1]和arr[mid...end],但整个数组不是递增的。其中i=begin,j=mid,k=end.
而后把i到mid-1的部分和mid到j-1的部分对调(可经过三次逆序实现)较小部分就调到前面去了,此时数组变为0 1 2 3 4 5 6 9 7 8(前面有序了,后面又是两个递增子数组,继续迭代便可)
D)多路归并排序(外部排序)
外部排序是指大文件的排序,即待排序的记录存储在外部存储器上,待排序的文件没法一次装入内存,须要在内存和外部存储器之间进行屡次数据交换,以达到排序整个文件的目的。
思路:外部排序最经常使用的算法是多路归并排序,即将源文件分解成多个可以一次性装入内存的部分,分别把每一部分调入内存完成排序,而后对已排序的子文件进行归并排序。
从二路到多路,增大k能够减小外存信息读写时间,但k个归并段中选择最小的记录须要比较k-1次,为了下降选出每一个记录须要的比较次数k,引入败者数。
败者树可视为一棵彻底二叉树,每一个叶结点存放各归并段在归并过程当中当前参加比较的记录,内部结点用来记忆左右子树中的失败者,让胜者网上继续进行比较,一直到根节点。若是比较两个数,大的为失败者,小的为胜利者,则根节点指向的数为最小数。
图中第一个叶子结点为b0.k路归并的败者树深度为
案例:有20个有序数组,每一个数组有500个unsigned int元素,降序排序。要求从这10000个元素中选出最大的500ge.
思路:依此从20个有序数组中选择一个当前元素,两两比较,而后找出最大的数,循环500次,便可选择出500个最大的数。可是这里每选择一个最大元素,须要比较19次,效率低。
改进方法1:利用堆,从20个数组中各取一个数,并记录每一个数的来源数组,创建一个含有20个元素的大顶堆。此时堆顶就是最大元素,去除堆顶元素,并从堆顶元素的来源数组中取下一个元素加入堆,调整堆后再取最大值,一直这样进行500次便可。时间复杂度
改进方法2:利用败者树。从20个数组中各取一个数,并记录每一个数的来源数组,创建一个20路归并的败者树。此时败者树输出的就是最大的数,而后从最大数的来源数组继续取下一个数加入败者树,继续比较,直到输出500个数为止。时间复杂度为
不一样排序算法的比较
总结:
1.比较次数和初始排列无关的是选择排序。
2.在初始序列基本有序的状况下,最优的是插入排序,此时插入排序时间复杂度为O(n),其次是冒泡排序,时间复杂度也为O(n).快速排序此时性能最差,时间复杂度为
3.堆排序对初始数据集的排列顺序不敏感,在最好,最坏和平均状况下,堆排序的时间复杂度为
4.节俭排序,一对数字不进行两次或两次以上的比较。包括(插入排序,归并排序)
5.基于比较的排序算法时间复杂度的下界(最好的时间复杂度)为: