题目:有元素按照递增有序排列的两个数组arr,和brr,将brr的元素合并到arr中,且arr中的元素依然有序。arr的大小足够存放arr的有效元素和brr的有效元素。
例如:
arr[10] = {1,2,3};m=3//m为arr的有效数据个数
brr[7] = {1,2,3,4,5,6,7};n = 3//n为brr的数组长度(元素都有效)
合并后:arr[10] = {1,1,2,2,3,3,4,5,6,7};
主要讲述三种方法:
1.空间换时间(O(n),S(n))
2.在arr原数组操作,从前向后合并arr和brr数组(O(n^2),S(1))
3.在arr原数组操作,从后向前合并arr和brr数组(O(n),S(1))
一、空间换时间
思想:申请一个新空间大小,刚好够arr和brr数组有效数据存放。将arr和brr数据合并到新空间中,再将新空间数组赋值到arr中。
bool Merge_1(int* arr, int m, int* brr, int n, int arrlen)//m为arr有效数据个数,n为brr有效数据个数,arrlen为arr能够存储数据的个数
{
if (m + n > arrlen) return false;//arr空间必须足够存放arr和brr的有效数据
int* newdata = (int*)malloc(sizeof(int) * arrlen);
if (newdata == NULL) return false;
int i = 0;//arr下标
int j = 0;//brr下标
int k = 0;//newdata下标
while (i < m && j < n)//依次将arr和brr中的较小的数据放入到newdata中
{
newdata[k++] = arr[i] < brr[j] ? arr[i++] : brr[j++];
}
while (i < m)//如果arr有剩余数据,将arr剩余数据放到newdata后面
{
newdata[k++] = arr[i++];
}
while (j < n)//如果brr有剩余数据,将brr剩余数据放到newdata后面
{
newdata[k++] = brr[j++];
}
memcpy(arr, newdata, sizeof(int)*arrlen);//将newdata的数据拷贝到arr里面
delete newdata;//释放堆空间
return true;
}
二、将arr和brr的元素从前向后合并到arr中
思想:i和j分别代表arr和brr待合并的数据的第一个数据下标
如果arr[i] <= brr[j],i就像后走,如果遇到arr[i] > brr[j]就将arr的i开始向后的数据向后移一位,将brr[j]赋值到arr[i]中,i++指向arr中待合并的数据的第一位,j++指向brr中下一个待合并的数据。如下图,arr[i] > brr[j]
如下图,将arr中的i开始的数据向后移一个单元格:
如下图,将brr[j]放到arr[i]中:
然后i和j都向后移一位,指向待合并的第一个元素(蓝色为已合并,红色为待合并),如下图:
重复上面步骤,代码如下:
//在原数组中操作,从两个数组的前面开始合并
bool one_move(int* arr, int len, int start,int last)//将下标为[start,last]的元素向后后移一个位置,不考虑last后的数据被覆盖的问题
{
if (start < 0 || start > last || last >= len - 1) return false;
for (int i = last; i >= start; i--)
{
arr[i + 1] = arr[i];
}
return true;
}
bool Merge_2(int* arr, int m, int* brr, int n, int arrlen)
{
if (m + n > arrlen) return false;//arr空间必须足够存放arr和brr的有效数据
int i = 0;
int j = 0;
while (i < m && j < n)
{
if (arr[i] > brr[j])//说明i的位置应该放brr[j],将arr的i开始的数据后移一个位置,i位置放brr[j]
{
if (one_move(arr, arrlen, i, m - 1) != true)//将arr的下标为[i,m-1]的数据后移一个位置
{
return false;
}
arr[i] = brr[j];//将brr[j]的数据放到arr[i]位置上
i++;
j++;
m += 1;//arr有效数据加1
}
else//说明i的位置就应该还是arr[i]
{
i++;
}
}
while (j < n)//说明arr的i走到了arr现有有效数据的最后一个位置的后面,将brr剩余的数据直接赋值到i开始向后的位置
{
arr[i++] = brr[j++];
}
return true;
}
三、从后向前合并数组arr和brr
思想:i为arr待合并数据的最后一个元素下标,j为brr待合并数据的最后一个元素下标,k为arr和brr元素合并到arr后,最后一个元素的下标,如下图:
如果arr[i] >= brr[j],将arr[i]赋值给arr[k],然后k和i都减减;
如果arr[i] < brr[j],将brr[j]赋值给arr[k],然后k和j都减减。
第一次的时候arr[i] == 3, brr[j] == 7,所以将brr[j]赋值给arr[k],然后k和j减减。
如下图:
然后再比较arr[i]和brr[j]值,重复 以上步骤,到最后:
上面这种情况属于arr元素都合并完了,brr还剩数据,将brr剩余数据都赋值给arr[k],然后k和j都减减就好了,直到brr合并完。还一种情况属于i没有走到头,但是j走到头了,说明brr合并完了,那么这个时候就合并完毕。
代码:
//在原数组中操作,从arr和brr的后面开始合并
bool Merge_3(int* arr, int m, int* brr, int n, int arrlen)
{
if (m + n > arrlen) return false;//arr空间必须足够存放arr和brr的有效数据
int i = m - 1;//arr下标
int j = n - 1;//brr下标
int k = m + n - 1;//合并后的最后一个元素下标
while (j >= 0 && i >= 0)//arr元素先合并完或者brr元素先合并完,那么就退出
{
arr[k--] = arr[i] > brr[j] ? arr[i--] : brr[j--];
}
while (j >= 0)//如果brr还剩数据,说明arr合并完了,将brr剩余元素继续合并到arr前面就好了
{
arr[k--] = brr[j--];
}
}