插入排序 和 归并排序(分治)的c实现和时间复杂度分析

来源:互联网 发布:网络诈骗学生自杀 编辑:程序博客网 时间:2024/05/01 23:46

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到midmerge(a, mid + 1, end);//递归排序mid + 1 到 endmergeswap(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)。


 

原创粉丝点击