算法设计与分析---合并有序数组(时空)O(n)+O(1)

设子数组A[0:k]和A[k+1:N-1]已排好序(0≤K≤N-1)。试设计一个合并这2个子数组为排好序的数组A[0:N-1]的算法。要求算法在最坏情况下所用的计算时间为O(N),只用到O(1)的辅助空间。

这道题难点在于辅助空间为O(1),如果不是O(1)的话利用非比较排序能实现O(n)排序。

  这道题突破口在于两个有序数组,前不了我了解到两个有序数组合并成一个有序数组可以使用插入排序进行归并,算法时间复杂度是O(n),(合并排序中的merge方法),因为在将b[j]插入新数组时只需要和a[i]比较,如果b[j] > a[i],由插入排序的性质可知b[j]比a[0..i-1]的任一元素都要大,所以直接放入a[i]后面即可,否则a[i]就放在b[j]之后,通过这样的办法就少了一层循环,达到O(n)。那么这道题也利用插入排序的思想试试吧。

  首先用 i 指向第一个子数组的首位置,j指向第二个子数组的首位置,由于这道题没有O(n)的辅助空间,所以我们的想法需要变一变,维护一个串a[0..i],对于序偶(a[0], a[1]), (a[1], a[2]), ..(a[i-1], a[i])都满足“<”关系。同时利用一个变量idx维护串a[idx..j],其表示为小于a[i]的串。我们利用i、j遍历数组,当 a[i] <= a[j] 时,i指针后移;当a[i] > a[j]时,令inx = j,维护串a[inx..j] 并插入a[i-1]到a[i]之间。

  图例如下:

    

void reverse(int a[],int n,int l,int mid,int r){
	for(int i=l,j=mid-1;i<j;i++,j--)swap(a[i],a[j]);
	for(int i=mid,j=r;i<j;i++,j--)swap(a[i],a[j]);
	for(int i=l,j=r;i<j;i++,j--)swap(a[i],a[j]);
}

void merge(int a[],int n,int k){
	int i=0,j=k,mid=k;
	while(j<n&&i<j){
		if(a[i]>a[j]){
			mid=j;
			while(j<n&&a[j]<a[i])j++;
			reverse(a,n,i,mid,j-1);
			i+=(j-mid);
		}
		i++;
	}
}
发布了200 篇原创文章 · 获赞 38 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_43746332/article/details/104932639