您的位置:首页 > 其它

插入排序 和 归并排序(分治)的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)。

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐