排序算法总结

时间:2024.5.8

所谓排序,就是要整理文件中的记录,使之按关键字递增(或递减)次序排列起来。当待排序记录的关键字都不相同时,排序结果是惟一的,否则排序结果不惟一。

在待排序的文件中,若存在多个关键字相同的记录,经过排序后这些具有相同关键字的记录之间的相对次序保持不变,该排序方法是稳定的;若具有相同关键字的记录之间的相对次序发生改变,则称这种排序方法是不稳定的。

要注意的是,排序算法的稳定性是针对所有输入实例而言的。即在所有可能的输入实例中,只要有一个实例使得算法不满足稳定性要求,则该排序算法就是不稳定的。

稳定的:直接插入排序,冒泡排序,归并排序

不稳定的:希尔排序,直接选择排序,堆排序,快速排序,

一.插入排序

插入排序的基本思想是每步将一个待排序的记录按其排序码值的大小,插到前面已经排好的文件中的适当位置,直到全部插入完为止。插入排序方法主要有直接插入排序和希尔排序。

①.直接插入排序(稳定)

接插入排序的过程为:在插入第i个记录时,R1,R2,..Ri-1已经排好序,将第i个记录的排序码Ki依次和R1,R2,..,Ri-1的排序码逐个进行比较,找到适当的位置。使用直接插入排序,对于具有n个记录的文件,要进行n-1趟排序。

代码如下:

void Dir_Insert(int A[],int N) //直接插入排序

{

int j,t;

for(int i=1;i<N;i++)

{

t=A[i];

j=i-1;

while(A[j]>t)

{

A[j+1]=A[j];

j--;

}

A[j+1]=t;

}

}

②.希尔排序(不稳定):

希尔(Shell)排序的基本思想是:先取一个小于n的整数d1作为第一个增量把文件的全部记录分成d1个组。所有距离为d1的倍数的记录放在同一个组中。先在各组内进行直接插入排序;然后,取得第二个增量d2<d1重复上述的分组和排序,直至所取的增量di=1,即所

有记录放在同一组中进行直接插入排序为止。该方法实质上是一种分组插入方法。 一般取d1=n/2,di+1=di/2。如果结果为偶数,则加1,保证di为奇数。

希尔排序是不稳定的,希尔排序的执行时间依赖于增量序列,其平均时间复杂度为O(n^1.3).

代码如下:

void Shell(int A[],int n) //Shell排序

{

int i,j,k,t;

(n/2)%2 == 0 ? k = n/2+1 : k = n/2; //保证增量为奇数

while(k > 0)

{

for(j=k;j<n; j++)

{

t = A[j];

i = j - k;

while(i>=0 && A[i]>t)

{

A[i+k]=A[i];

i=i-k;

}

A[i+k]=t;

}

if(k == 1) break;

(k/2)%2 ==0 ? k=k/2+1 : k=k/2;

}

}

二.选择排序

选择排序的基本思想是每步从待排序的记录中选出排序码最小的记录,顺序存放在已排序的记录序列的后面,直到全部排完。选择排序中主要使用直接选择排序和堆排序。

①.直接选择排序(不稳定)

直接选择排序的过程是:首先在所有记录中选出序码最小的记录,把它与第1个记录交换,然后在其余的记录内选出排序码最小的记录,与第2个记录交换......依次类推,直到所有记录排完为止。

无论文件初始状态如何,在第i趟排序中选出最小关键字的记录,需要做n-i次比较,因此,总的比较次数为n(n-1)/2=O(n^2)。当初始文件为正序时,移动次数为0;文件初态为反序时,每趟排序均要执行交换操作,总的移动次数取最大值3(n-1)。直接选择排序的平均时间复杂度为O(n^2)。直接选择排序是不稳定的。

代码如下:

void Dir_Choose(int A[],int n) //直接选择排序

{

int k,t;

for(int i=0;i<n-1;i++)

{

k=i;

for(int j=i+1;j<n;j++)

{

if(A[j]<A[k]) k=j;

}

if(k!=i)

{

t=A[i];

A[i]=A[k];

A[k]=t;

}

}

}

②.堆排序(不稳定)

堆排序是一种树形选择排序,是对直接选择排序的有效改进。n个关键字序列

K1,K2,...,Kn称为堆,当且仅当该序列满足(Ki<=K2i且Ki<=K2i+1)或(Ki>=K2i且Ki>=K2i+1),(1<=i<=n/2)。根结点(堆顶)的关键字是堆里所有结点关键字中最小者,称为小根堆;根结点的关键字是堆里所有结点关键字中最大者,称为大根堆。

若将此序列所存储的向量R[1..n]看作是一棵完全二叉树的存储结构,则堆实质上是满足如下性质的完全二叉树:树中任一非叶结点的关键字均不大于(或不小于)其左右孩子(若存在)结点的关键字。

堆排序的关键步骤有两个:一是如何建立初始堆;二是当堆的根结点与堆的最后一个结点交换后,如何对少了一个结点后的结点序列做调整,使之重新成为堆。堆排序的最坏时间复杂度为O(nlog2n),堆排序的平均性能较接近于最坏性能。由于建初始堆所需的比较 次数较多,所以堆排序不适宜于记录较少的文件。堆排序是就地排序,辅助空间为O(1),它是不稳定的排序方法。

代码略..

三.交换排序

交换排序的基本思想是:两两比较待排序记录的排序码,并交换不满足顺序要求的那写偶对,直到满足条件为止。交换排序的主要方法有冒泡排序和快速排序.

①.冒泡排序(稳定的)

冒泡排序将被排序的记录数组R[1..n]垂直排列,每个记录R[i]看作是重量为ki的气泡。

根据轻气泡不能在重气泡之下的原则,从下往上扫描数组R;凡扫描到违反本原则的轻气泡,就使其向上"漂浮"。如此反复进行,直到最后任何两个气泡都是轻者在上,重者在下为止。 冒泡排序的具体过程如下:

第一步,先比较k1和k2,若k1>k2,则交换k1和k2所在的记录,否则不交换。继续对k2和k3重复上述过程,直到处理完kn-1和kn。这时最大的排序码记录转到了最后位置,称第1次起泡,共执行n-1次比较。

与第一步类似,从k1和k2开始比较,到kn-2和kn-1为止,共执行n-2次比较。 依次类推,共做n-1次起泡,完成整个排序过程。

若文件的初始状态是正序的,一趟扫描即可完成排序。所需关键字比较次数为n-1次,记录移动次数为0。因此,冒泡排序最好的时间复杂度为O(n)。

若初始文件是反序的,需要进行n-1趟排序。每趟排序要进行n-i次关键字的比较(1<=i<=n-1),且每次比较都必须移动记录三次来达到交换记录位置。在这种情况下,比较次数达到最大值n(n-1)/2=O(n^2),移动次数也达到最大值3n(n-1)/2=O(n^2)。因此,冒泡排序的最坏时间复杂度为O(n^2)。

虽然冒泡排序不一定要进行n-1趟,但由于它的记录移动次数较多,故平均性能比直接插入排序要差得多。冒泡排序是就地排序,且它是稳定的。

代码如下:

void QP(int A[],int n) //优化的冒泡排序

{

int count=0,t,flag;

for(int i=0;i<n-1;i++)

{

flag=0;

for(int j=0;j<n-i;j++)

{

if(A[j+1]<A[j])

{

t=A[j];

A[j]=A[j+1];

A[j+1]=t;

flag=1;

count+=3;

}

}

if(flag==0) break;

}

}

②.快速排序:(不稳定的)

快速排序采用了一种分治的策略,通常称其为分治法,其基本思想是:将原问题分解为若干个规模更小但结构与原问题相似的子问题。递归地解这些子问题,然后将这些子问题的

解组合为原问题的解。 快速排序的具体过程如下:

第一步,在待排序的n个记录中任取一个记录,以该记录的排序码为准,将所有记录分成两组,第1组各记录的排序码都小于等于该排序码,第2组各记录的排序码都大于该排序码,并把该记录排在这两组中间。

第二步,采用同样的方法,对左边的组和右边的组进行排序,直到所有记录都排到相应的位置为止。

代码如下:

void Quick_Sort(int A[],int low,int high) //low和high是数组的下标

{

if(low<high)

{

int temp,t=A[low];

int l=low,h=high;

while(l<h)

{

while(A[l]<t) l++;

while(A[h]>=t) h--;

if(h>l)

{

temp=A[l];

A[l]=A[h];

A[h]=temp;

}

}

Quick_Sort(A,low,l-1);

Quick_Sort(A,l+1,high);

}

}

四.归并排序

归并排序是将两个或两个以上的有序子表合并成一个新的有序表。初始时,把含有n个结点的待排序序列看作由n个长度都为1的有序子表组成,将它们依次两两归并得到长度为2的若干有序子表,再对它们两两合并。直到得到长度为n的有序表,排序结束。

归并排序是一种稳定的排序,可用顺序存储结构,也易于在链表上实现,对长度为n的文件,需进行log2n趟二路归并,每趟归并的时间为O(n),故其时间复杂度无论是在最好情况下还是在最坏情况下均是O(nlog2n)。归并排序需要一个辅助向量来暂存两个有序子文件归并的结果,故其辅助空间复杂度为O(n),显然它不是就地排序。

代码略...

五.基数排序

设单关键字的每个分量的取值范围均是C0<=Kj<=Crd-1(0<=j<=rd),可能的取值个数rd称为基数.基数的选择和关键字的分解因关键字的类型而异.

(1).若关键字是十进制整数,则按个、十等位进行分解,基数rd=10,C0=0,C9=9,d为最长整数的位数.

(2).若关键字是小写的英文字符串,则rd=26,C0='a',C25='z',d为最长字符串的长度. 基数排序的基本思想是:从低位到高位依次对待排序的关键码进行分配和收集,经过d趟分配和收集,就可以得到一个有序序列.

按平均时间将排序分为四类:

(1)平方阶(O(n2))排序

一般称为简单排序,例如直接插入、直接选择和冒泡排序;

(2)线性对数阶(O(nlgn))排序

如快速、堆和归并排序;

(3)O(n1+£)阶排序

£是介于0和1之间的常数,即0<£<1,如希尔排序;

(4)线性阶(O(n))排序

如基数排序。

各种排序方法比较

简单排序中直接插入最好,快速排序最快,当文件为正序时,直接插入和冒泡均最佳。

影响排序效果的因素

因为不同的排序方法适应不同的应用环境和要求,所以选择合适的排序方法应综合考虑下列因素:

①待排序的记录数目n;

②记录的大小(规模);

③关键字的结构及其初始状态;

④对稳定性的要求;

⑤语言工具的条件;

⑥存储结构;

⑦时间和辅助空间复杂度等。

不同条件下,排序方法的选择

(1)若n较小(如n≤50),可采用直接插入或直接选择排序。

当记录规模较小时,直接插入排序较好;否则因为直接选择移动的记录数少于直接插人,应选直接选择排序为宜。 (2)若文件初始状态基本有序(指正序),则应选用直接插人、冒泡或随机的快速排序为宜;

(3)若n较大,则应采用时间复杂度为O(nlgn)的排序方法:快速排序、堆排序或 归并排序。

快速排序是目前基于比较的内部排序中被认为是最好的方法,当待排序的关键字是随机分布时,快速排序的平均时间最短;

堆排序所需的辅助空间少于快速排序,并且不会出现快速排序可能出现的最坏情况。这两种排序都是不稳定的。

若要求排序稳定,则可选用归并排序。但从单个记录起进行两两归并的 排序算法并不值得提倡,通常可以将它和直接插入排序结合在一起使用。先利用直接插入排序求得较长的有序子文件,然后再两两归并之。因为直接插入排序是稳定的,所以改进后的归并排序仍是稳定的


第二篇:JVM调优总结(三)基本垃圾回收算法


可以从不同的的角度去划分垃圾回收算法:

按照基本回收策略分

引用计数(Reference Counting):

比较古老的回收算法。原理是此对象有一个引用,即增加一个计数,删除一个引用则减少一个计数。垃圾回收时,只用收集计数为0的对象。此算法最致命的是无法处理循环引用的问题。

标记-清除(Mark-Sweep):

此算法执行分两阶段。第一阶段从引用根节点开始标记所有被引用的对象,第二阶段遍历整个堆,把未标记的对象清除。此算法需要暂停整个应用,同时,会产生内存碎片。

复制(Copying):

此算法把内存空间划为两个相等的区域,每次只使用其中一个区域。垃圾回收时,遍历当前使用区域,把正在使用中的对象复制到另外一个区域中。次算法每次只处理正在使用中的对象,因此复制成本比较小,同时复制过去以后还能进行相应的内存整理,不会出现“碎片”问题。当然,此算法的缺点也是很明显的,就是需要两倍内存空间。

标记-整理(Mark-Compact):

此算法结合了“标记-清除”和“复制”两个算法的优点。也是分两阶段,第一阶段从根节点开始标记所有被引用对象,第二阶段遍历整个堆,把清除未标记对象并且把存活对象“压缩”到堆的其中一块,按顺序排放。此算法避免了“标记-清除”的碎片问题,同时也避免了“复制”算法的空间问题。

按分区对待的方式分

增量收集(Incremental Collecting):实时垃圾回收算法,即:在应用进行的同时进行垃圾回收。不知道什么原因JDK5.0中的收集器没有使用这种算法的。

分代收集(Generational Collecting):基于对对象生命周期分析后得出的垃圾回收算法。把对象分为年青代、年老代、持久代,对不同生命周期的对象使用不同的算法(上述方式中的一个)进行回收。现在的垃圾回收器(从J2SE1.2开始)都是使用此算法的。

按系统线程分

串行收集:串行收集使用单线程处理所有垃圾回收工作,因为无需多线程交互,实现容易,而且效率比较高。但是,其局限性也比较明显,即无法使用多处理器的优势,所以此收集适合单处理器机器。当然,此收集器也可以用在小数据量(100M左右)情况下的多处理器机器上。

并行收集:并行收集使用多线程处理垃圾回收工作,因而速度快,效率高。而且理论上CPU数目越多,越能体现出并行收集器的优势。

并发收集:相对于串行收集和并行收集而言,前面两个在进行垃圾回收工作时,需要暂停整个运行环境,而只有垃圾回收程序在运行,因此,系统在垃圾回收时会有明显的暂停,而且暂停时间会因为堆越大而越长。

更多相关推荐:
c语言 排序算法总结

排序算法总结选择法排序:for(i=0;i9;i++){max=i;for(j=i+1;j10;j++)if(a[max]a[j])max=j;/*max为查找范围最大数所在元素下标*/if(max!=i)(i…

c语言排序算法总结(主要是代码实现)

冒泡排序(BubbleSort)是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。…

排序算法总结

按平均时间将排序分为四类:(1)平方阶(O(n2))排序一般称为简单排序,例如直接插入、直接选择和冒泡排序;(2)线性对数阶(O(nlgn))排序如快速、堆和归并排序;(3)O(n1+£)阶排序£是介于0和1之…

计算机考研 数据结构 查找和排序算法总结

查找算法总结静态查找n1顺序表的查找-顺序查找:查找成功时的平均查找长度ASL=n?n?i?1=i?1(n?1)2,查找不成功时的比较次数为n+1,故查找成功与不成功等概率时的平均查找长度为12n?(n?i?1…

八大排序算法总结

八大排序算法总结插入排序1.直接插入排序原理:将数组分为无序区和有序区两个区,然后不断将无序区的第一个元素按大小顺序插入到有序区中去,最终将所有无序区元素都移动到有序区完成排序。要点:设立哨兵,作为临时存储和判…

排序算法总结

现有序列{9,3,5,1,6,2,8,4,7},以此为例子,阐述各个常用排序算法。直接插入排序:每次从无序表中取出第一个元素,把它插入到有序表的合适位置,使有序表仍然有序。第一趟比较前两个数,然后把第二个数按大…

用php实现的各种排序算法总结

用php实现的各种排序算法总结优化php性能的五个实用技巧:以下是五个优化技巧,熟练掌握后对于开发还是很有帮助的。1.对字符串使用单引号PHP引擎允许使用单引号和双引号来封装字符串变量,但是这个是有很大的差别的…

排序算法总结

一插入排序InsertionSort1基本思想每次将一个待排序的数据元素插入到前面已经排好序的数列中的适当位置使数列依然有序直到待排序数据元素全部插入完为止2排序过程示例初始关键字493865977613274...

排序算法总结

3排序算法总结31直接插入排序顺序遍历元素并将其插入到其之前已经有序的数组中在最好情况下即已经全部按所需的次序排列好则比对次数仅为n1记录无需移动时间复杂度为On1在最坏情况下即已经全部按所需的次序反序排列好则...

排序算法总结

排序算法总结一插入排序InsertionSort1基本思想每次将一个待排序的数据元素插入到前面已经排好序的数列中的适当位置使数列依然有序直到待排序数据元素全部插入完为止2排序过程示例初始关键字493865977...

10种排序算法总结

10种排序算法总结排序算法有很多所以在特定情景中使用哪一种算法很重要为了选择合适的算法可以按照建议的顺序考虑以下标准1执行时间2存储空间3编程工作对于数据量较小的情形12差别不大主要考虑3而对于数据量大的1为首...

排序算法总结

排序算法总结一插入排序InsertionSort1基本思想每次将一个待排序的数据元素插入到前面已经排好序的数列中的适当位置使数列依然有序直到待排序数据元素全部插入完为止2排序过程示例初始关键字493865977...

排序算法总结(64篇)