插入排序 和 归并排序(分治)的c实现和时间复杂度分析
2012-12-20 11:41
330 查看
1、排序算法
输入:n个数<a1,a2,a3,a4,...,an>。输出:输入序列的一个排序<a1',a2',...,an'> ,满足a1' <= a2' <= ...<= an'。
2、插入排序
插入排序是一个对少量元素排序的有效算法。其算法思想为:对一个序列<a1,a2,...,an>,从i=2开始依次与前面i-1的元素进行比较,将ai插入a1到ai-1中,使得a1-ai有序,当i=n时,完成an的插入即可得到排序后序列。void InsertSort(int a[],int length) { int i,j,tmp; for(i = 1; i < length; i++) { //从a[1]循环到a[n-1] for(j = 0; j < i; j++) { //a[0]到a[i-1]的循环,其中a[0]到a[i-1]已经排好序了。 if(a[j] > a[i]) { //当找到一个a[j]大于a[i],交换两值,使得a[0]到a[i]排好序。 tmp = a[j]; //可封装成swap(int &a,int &b)。 a[j] = a[i]; a[i] = tmp; } } } }
时间复杂度
可以知道,对一个n位的数组,需要进行n-1次排序迭代。第一次迭代需要比较a1和a2的大小,第二次需要比较a3和a1,a2的大小,当a3小于a1,则不用比较a3和a2了(升序排序)。反正需要2次比较。第k次迭代最坏情况需要循环比较k次,平均情况为k/2次。
最坏情况总需要循环比较的次数为1+2+...+n-1=n(n-1)/2。平均情况需要1+2/2+...+(n-1)/2=n(n-1)/4。
即时间最坏和平均复杂度都为O(n^2)。
3、合并排序(分治法)
合并排序采用递归的方法,对一个序列<a1,a2,...,an>,递归排序<a1,...,aq>序列和<aq+1,...,an>序列,然后合并为<a1,...,an>的序列。合并时相当于对<a1',a2',....,aq',...,an'>进行排序,其中<a1',...,aq'>和<aq+1',...,an'>已经排好序了。void mergeswap(a,begin, mid, end) { int i, j, k = 0; int length; length = end - begin + 1; int *tmp = malloc(length * sizeof(a[0])); //分配与a数组等大空间存储拍好的序列 for (i = begin, j = mid + 1; i <=mid && j <= end;/*null*/) { //此循环将两个排好序的数组按大小放入tmp中,直到某个序列全部放入 tmp[k++] = (a[i] <= a[j]) ? a[i++] : a[j++]; //tmp[k++]:先执行tmp[k],后k++ } for(/*null*/;j <= end;/*null*/) //上个for循环后有j>end或i>mid,即这两个循环只会执行其中一个 tmp[k++] = a[j++]; for(/*null*/;i <= mid;/*null*/) tmp[k++] = a[i++]; for(i = 0; i < length; i++) //将排好序的数组填入原数组中 a[begin + i] = tmp[i]; free(tmp); //内存释放 } void merge(int a[], int begin, int end) { if(begin >= end) //大于情况为输入错误,等于为对一个数排序。 return; int mid = (begin + end) / 2; //q取数组的中间值,整数除法向下取整 merge(a, begin, mid); //递归排序begin到mid merge(a, mid + 1, end); //递归排序mid + 1 到 end mergeswap(a, begin, mid, end); //对数组a排序,其中begin到mid,mid+1到end分别有序 } void MergeSort(int a[], int length) //接口函数 { merge(a, 0, length - 1); //实际算法函数,注意输入为数组的序号 }
另一种mergeswap方法(哨兵法)
void mergeswap(int a[], int begin, int mid, int end) { int i, j, k = 0; int length, Llength, Rlength; length = end - begin + 1; Llength = mid - begin + 1; Rlength = end - mid; int *L = malloc((Llength + 1) * sizeof(a[0])); int *R = malloc((Rlength + 1) * sizeof(a[0])); for(i = 0; i < Llength; i++) L[i] = a[begin + i]; for(j = 0; j < Rlength; j++) R[j] = a[mid + j + 1]; L[Llength] = R[Rlength] = a[mid] + a[end] + 1; //使得L和R的最后一个数为最大值,保证某个数组完全填入a时,另一个数组中的值都比较小 for(i = 0, j = 0, k = 0; k < length; k++) { a[k++] = (L[i] <= R[j]) ? L[i++] : R[j++]; } free(L); free(R); }
时间复杂度
对一个n位的数组,需要递归logn次(每次递归位数除2,最后一次为位数为1),第k次递归(起始算第0次递归)需要对2^k个位数为n/(2^k) 个数值进行排序。从代码可以看出第k次排序需要对每个数组循环2*n/(2^k) 次(n/(2^k) 次对tmp赋值,n/(2^k) 次对a数组的写入),即每次递归需要循环2^k
* 2*n/(2^k) = 2n次,总循环次数为为 nlogn,每次循环都是必须的(数组本身序列只影响循环内计算),故最坏和平均时间复杂度都为O(nlogn)。
相关文章推荐
- 快速排序和冒泡排序的时间复杂度分析(C++算法实现对比)
- 插入排序的时间复杂度分析
- 插入排序和归并排序实现以及时间复杂度分析
- 归并排序实现及时间复杂度分析
- 直接插入排序算法c语言实现。 己它的时间复杂度分析。
- java版排序算法简介及冒泡排序以及优化,选择排序,直接插入排序,希尔排序,堆排序,快速排序及其优化前言 2 分类 2 稳定性 3 时间复杂度 4 Java实现版本 5 1、冒泡排序 6 2、选择排序
- c语言实现线性表的建立,初始化,插入,删除,查找,遍历以及时间复杂度分析
- 回首Java——八大排序以及冒泡排序,插入排序分析实现
- 一步步学习数据结构和算法之直接插入排序效率分析及java实现
- 冒泡,选择,插入,时间复杂度O(n2)算法感悟(此博文属于对于排序过程有大概了解的同学)
- 快速排序的实现与时间复杂度
- MIT:算法导论——4.2快速排序 以及 排序算法时间复杂度分析
- (数据结构与算法分析 八)------插入排序,希尔排序,归并排序的实现( Java语言描述)
- 元素排序几种常用的排序算法的分析及java实现(希尔排序,堆排序,归并排序,快速排序,选择排序,插入排序,冒泡排序)
- java实现各种基础排序(冒泡排序、快速排序、直接选择排序、堆排序、直接插入排序、归并排序)
- 插入排序及归并排序java代码实现及详细注释
- C++实现几种常用的时间复杂度为O(nlogn)的排序方法:归并排序、快速排序、堆排序、希尔排序
- 快速排序的C语言实现及其时间复杂度
- 插入排序、冒泡排序、选择排序、希尔排序、快速排序、归并排序、堆排序和LST基数排序——JAVA实现
- 算法 插入排序 的 JS实现及时间复杂度分析