赞
踩
归并排序法是将两个或以上的有序表合并成一个新的有序表,即把待排序序列分成若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。注意:一定要是有序序列!
归并排序实例:
设r[i……n]由两个有序子表r[i….m]和r[m+1……n]组成,两个子表长度分别为m-i+1、n-m。
1. J=m+1 ;k=i ; I=i ; 置两个子表的起始下标及辅助数组的起始下标;
2. 若I>m 或J>n ,转(4),表示其中一个子表已合并完,比较选取结束;
3. //选取r[i]和r[j]较小的存入辅助数组rf
如果r[i] < r[j],rf[k]=r[i]; i++; k++; 转⑵
否则,rf[k]=r[j]; j++; k++; 转⑵
4.//选取r[i]和r[j]较小的存入辅助数组rf
如果r[i] < r[j],rf[k]=r[i]; i++; k++; 转⑵
否则,rf[k]=r[j]; j++; k++; 转⑵
5.合并结束
- void Merge(ElemType *r,ElemType *rf, int i, int m, int n)
- {
- int j,k;
- for(j=m+1,k=i; i<=m && j <=n ; ++k){
- if(r[j] < r[i]) rf[k] = r[j++];
- else rf[k] = r[i++];
- }
- while(i <= m) rf[k++] = r[i++];
- while(j <= n) rf[k++] = r[j++];
- }
归并的时间复杂度分析:主要考虑两个函数的时间花销,一、数组划分函数 二、有序数组归并函数
前者的时间复杂度为O(n),因为代码中有2个长度为n的循环,所以时间复杂度为O(n);
数组长度为n的归并排序所消耗的时间T[n]:调用前面函数将数组划分为两个部分,那每一个部分排序好所花时间为T[n/2],最后将这两个部分的数组合并成一个有序数组的时间花费为O(n);
公式:T[n]=2T[n/2]+O(n);
最终得出结果为:T[n]=O(nlogn);
因为不管元素在什么情况下都要做出这些步骤,所花销的时间是不变的,所以该算法的最优时间复杂度和最差时间复杂度以及平均复杂度是一致的:O(nlogn)。
不稳定的排序算法:选择排序、快速排序、希尔排序、堆排序;
稳定的排序算法: 冒泡排序,直接插入排序、归并排序等。。。
关于稳定性的记忆方法:
1、插入排序和交换排序各自的初始排序方式,也就是直接插入排序和冒泡排序都是稳定的排序。归并排序是优化排序方式中唯一一种稳定的排序方式。
2、希尔排序和快速排序是插入排序和交换排序的优化方式中的,不稳定的排序方式。
3、选择排序和堆排序是具有共同性质,就是不稳定性。
附录:
什么叫稳定性:通俗地讲就是能保证排序前2个相等的数其序列的前后位置顺序和排序后他们两个的前后位置顺序相同。在简单形式化一下,如果Ai=Aj,Ai原来在位置前,排序后Ai还是要在Aj位置前。
稳定性的好处:排序算法如果稳定,那么从一个键上排序,然后再从另一个键上排序,第一个键排序的结果可以为第二个键排序所用。
注意区分选择排序和堆排序的关系:
选择排序:基本思想就是依次从待排序中选择关键字最小的记录、次小的记录。。。。。并分别将他们定位到序列左侧的第一个位置,第二个位置。。。,从而使得序列是从小到大排序的序列。
直接选择排序:从第i个无序列表arr[i…n]中,选择关键字值最小的记录将其插入有序列表的末尾arr[n-i+1],交换一次位置。–是不稳定排序。
堆排序:满足完全二叉树特性,其左右子树分别是堆,任何一个结点的值不大于或不小于左右孩子结点的值;堆排序分别称为小顶堆和大顶堆。
关于时间复杂度的记忆方法:
1、初始排序方式时间复杂度基本上都是O(n^2),其中包括插入排序,冒泡排序,选择排序。注意冒泡排序和插入排序的最好时间复杂度为O(n);
2、优化的排序方式的时间复杂度基本上都是O(nlogn),其中包括快速排序,堆排序和归并排序。注意快速排序的最坏情况类似于冒泡排序,因此时间复杂度为O(n^2);
3、希尔排序是个特例,无法计算它的时间复杂度,因为与增量因子序列dk的选取有关。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。