两个有序数组合并成一个有序数组

百度2012实习生校园招聘笔试题

数组al[0,mid]和al[mid+1,num-1]是各自有序的,对数组al[0,num-1]的两个子有序段进行merge,得到al[0,num-1]整体有序要求空间复杂度为O(1)。注:al[i]元素是支持'<'运算符的。

1、用temp数组作为中间变量,保存两个有序子数组的合并结果数组,再复制回原数组。

//将有二个有序数列a[first...mid]和a[mid...last]合并。
void mergearray(int a[], int first, int mid, int last, int temp[])
{
	int i = first, j = mid + 1;
	int m = mid,   n = last;
	int k = 0;
	
	while (i <= m && j <= n)
	{
		if (a[i] <= a[j])
			temp[k++] = a[i++];
		else
			temp[k++] = a[j++];
	}
	
	while (i <= m)
		temp[k++] = a[i++];
	
	while (j <= n)
		temp[k++] = a[j++];
	
	for (i = 0; i < k; i++)
		a[first + i] = temp[i];
}

2、没有中间变量temp,要求空间复杂度为O(1),通过交换+移位实现
      空间复杂度O(1),时间复杂度最坏为O(N*N)
void swap( int *a,int *b)
{
	int temp;
	temp=*a;
	*a=*b;
	*b=temp;
}

void MergeSortedArray02(int *a,int first,int mid,int last)  //不使用中间数组temp,通过数组元素交换实现,空间复杂度O(1),时间复杂度最坏为O(N^2)
{
	int temp;
	int i=first,j=mid+1;
	while(i<=mid&&j<=last)
	{
		if(a[i]<=a[j])
		{
			i++;
		}
		else
		{
			swap(&a[i],&a[j]);   //交换位置
			for(int k=j-1;k>i;k--)
			{
				swap(&a[k],&a[k+1]); //从i+1到j的子数组循环右移一位,使得
			}
			i++;
			j++;
		}
	}
}


3、优化第二种方法,交换+循环移位(不用中间变量,通过交换反转实现),第二种方法中,每次移动的单位是一个数,效率低,可以采用块循环移动(单位是连续几个数)进行优化
空间复杂度O(1),时间复杂度应该比O(m+n)要小!

void Reverse(int *a , int begin , int end )   //反转
 {
     for(; begin < end; begin++ , end--)
         swap(a[begin] , a[end]);
 }
 void RotateRight(int *a , int begin , int end , int k)    //循环右移,a[begin:end]段向右循环移位k位
 {
     int len = end - begin + 1;  //数组的长度
     k %= len;
     Reverse(a , begin , end - k);
     Reverse(a , end - k + 1 , end);
     Reverse(a , begin , end);
 }
 
 // 将有序数组a[begin...mid] 和 a[mid+1...end] 进行合并
 void Merge(int *a , int begin , int end )
 {
     int i , j , k;
     i = begin;
     j = 1 + ((begin + end)>>1);    //位运算的优先级比较低,需要加一个括号
     while(i <= end && j <= end && i k)
             RotateRight(a , i , j-1 , j-k);   //数组a[i...j-1]循环右移j-k次,a[i..j-1]始终有序
         i += (j-k+1);  //第一个指针往后移动,因为循环右移后,数组a[i....i+j-k]是有序的
     }
 }

4、标准参考算法

 1 /* 
 2 数组a[begin, mid] 和 a[mid+1, end]是各自有序的,对两个子段进行Merge得到a[begin , end]的有序数组。 要求空间复杂度为O(1) 
 3 方案: 
 4 1、两个有序段位A和B,A在前,B紧接在A后面,找到A的第一个大于B[0]的数A[i], A[0...i-1]相当于merge后的有效段,在B中找到第一个大于A[i]的数B[j], 
 5 对数组A[i...j-1]循环右移j-k位,使A[i...j-1]数组的前面部分有序 
 6 2、如此循环merge 
 7 3、循环右移通过先子段反转再整体反转的方式进行,复杂度是O(L), L是需要循环移动的子段的长度 
 8 */  
 9 #include  
10 using namespace std;  
11   
12 void Reverse(int *a , int begin , int end )   //反转  
13 {  
14     for(; begin < end; begin++ , end--)  
15         swap(a[begin] , a[end]);  
16 }  
17 void RotateRight(int *a , int begin , int end , int k)    //循环右移  
18 {  
19     int len = end - begin + 1;  //数组的长度  
20     k %= len;  
21     Reverse(a , begin , end - k);  
22     Reverse(a , end - k + 1 , end);  
23     Reverse(a , begin , end);  
24 }  
25   
26 // 将有序数组a[begin...mid] 和 a[mid+1...end] 进行归并排序  
27 void Merge(int *a , int begin , int end )  
28 {  
29     int i , j , k;  
30     i = begin;  
31     j = 1 + ((begin + end)>>1);    //位运算的优先级比较低,外面需要加一个括号,刚开始忘记添加括号,导致错了很多次  
32     while(i <= end && j <= end && i k)  
40             RotateRight(a , i , j-1 , j-k);   //数组a[i...j-1]循环右移j-k次  
41         i += (j-k+1);  //第一个指针往后移动,因为循环右移后,数组a[i....i+j-k]是有序的  
42     }  
43 }  
44   
45 void MergeSort(int *a , int begin , int end )  
46 {  
47     if(begin == end)  
48         return ;  
49     int mid = (begin + end)>>1;  
50     MergeSort(a , begin , mid);   //递归地将a[begin...mid] 归并为有序的数组  
51     MergeSort(a , mid+1 , end);   //递归地将a[mid+1...end] 归并为有序的数组  
52     Merge(a , begin , end);       //将有序数组a[begin...mid] 和 a[mid+1...end] 进行归并排序  
53 }  
54   
55 int main(void)  
56 {  
57     int n , i ;  
58     //    int a[] = {1, 5,6,7,15,66, 2,3,4,8,33,35};  
59     int a[] = {1 ,6 ,9 , 2 ,3,4 ,15};  
60       
61     n = sizeof(a)/sizeof(int);  
62       
63     MergeSort(a , 0 , n - 1);  
64       
65     for(i = 0 ; i < n ; ++i)  
66         cout<


参考: http://www.verydemo.com/demo_c180_i19827.html

你可能感兴趣的:(C/C++)