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

百度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<j)
     {
         while(i <= end && a[i] < a[j])
             i++;
         k = j;   //暂时保存指针j的位置
         while(j <= end && a[j] < a[i])  // 找到连续的j索引列都小于a[i],进行块循环移动
             j++;
         if(j > 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<iostream>  
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<j)  
33     {  
34         while(i <= end && a[i] < a[j])  
35             i++;  
36         k = j;   //暂时保存指针j的位置  
37         while(j <= end && a[j] < a[i])  
38             j++;  
39         if(j > 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<<a[i]<<" ";  
67       
68     return 0;  
69 }  



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值