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

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)。


 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值