In place Merge(原地归并)
数组al[0,mid-1] 和 al[mid,num-1],都分别有序。将其merge成有序数组al[0,num-1],要求空间复杂度O(1)
思路:一般的归并是需要O(n)的空间,而这里要求空间复杂度为O(1),也就是只能使用常熟级别的临时变量。而原地操作无非就是移动,关键是怎么移动。在编程珠玑中有一个旋转算法(旋转后k个元素到前面),可以在这里起到关键性作用。见http://www.cnblogs.com/ivorfeng/archive/2013/05/12/3074822.html
因为数组al[0,mid-1] 和 al[mid,num-1],都分别有序,我们要做的是将较小的元素(可能是连续的一片)插入到合理的位置中;不妨假设left 和right是两个有序数组中的下标,a[left] < a[right],首先要在左半部分找到第一个比a[right]大的下标newleft,接着在右半部分找到比a[newleft]小的下表newright,并记录right到newright所移动的步数move
如上图所示,最后就是将红色框中的元素进行右旋转move(这里为3)个元素。
代码如下:
1 //翻转数组 2 template <typename T> 3 void Reverse(T *a, int size) 4 { 5 T tmp; 6 for(int i = 0; i < size/2; ++i) 7 { 8 tmp = a[i]; 9 a[i] = a[size - i - 1]; 10 a[size - i - 1] = tmp; 11 } 12 13 } 14 //旋转 15 template <typename T> 16 void Rotate(T*a, int size, int pos) 17 { 18 Reverse(a, pos); 19 Reverse(a + pos, size - pos); 20 Reverse(a, size); 21 22 } 23 template<typename T> 24 void InplaceMerge(T *a, int size, int mid) 25 { 26 int fir = 0; 27 int sec = mid; 28 int Move = 0; 29 while (fir < sec && sec < size) 30 { 31 while (fir < sec && a[fir] < a[sec]) 32 { 33 ++fir; 34 } 35 Move = 0; 36 while ( sec < size && a[sec] < a[fir]) 37 { 38 ++sec; 39 ++Move; 40 } 41 Rotate(a + fir, sec - fir, sec - fir - Move); 42 fir += Move; 43 } 44 }
这样空间复杂度主要是Reverse中用到,为O(1).
不经历风雨,怎么见彩虹!