两个有序数组合并成一个有序数组
百度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 }
参考:http://www.verydemo.com/demo_c180_i19827.html
声明:该文观点仅代表作者本人,牛骨文系教育信息发布平台,牛骨文仅提供信息存储空间服务。
- 上一篇: Form表单提交数据的几种方式
- 下一篇: [DB2 学习记录]7. 创建DB