给定两个有序整数数组 nums1 和 nums2,将 nums2 合并到 nums1 中,使得 num1 成为一个有序数组。
说明:
初始化 nums1 和 nums2 的元素数量分别为 m 和 n。
你可以假设 nums1 有足够的空间(空间大小大于或等于 m + n)来保存 nums2 中的元素。
示例:
输入:
nums1 = [1,2,3,0,0,0], m = 3(后面的三个0是额外的元素)
nums2 = [2,5,6], n = 3
输出:
[1,2,2,3,5,6]
(1)忽略nums1后面多余的元素,额外使用一个辅助数组C
时间O(m+n),空间复杂度O(m+n)
假设两个有序数组都是升序,新建数组C,长度为数组A和数组B的长度之和,从索引0出依次设置数组C对于下标的值
/**
* 合并两个有序数组
* 假设两个有序数组都是升序,新建数组C,长度为数组A和数组B的长度之和,从索引0出依次设置数组C对于下标的值
*/
public class SortedArrayCombine {
public static void main(String[] args) {
int[] a = {1, 3, 5, 7, 9, 11, 13};
int[] b = {2, 4, 6, 8, 10, 12};
int[] c = new int[a.length + b.length];
int i = 0;
int j = 0;
for (int k = 0; k < c.length; k++) {
if (i < a.length && j < b.length) {//a和b都没走完
if (a[i] <= b[j]) {
c[k] = a[i];
i++;
} else {
c[k] = b[j];
j++;
}
} else if (i < a.length) {//或者else if(i < a.length && j >= b.length) 也就是b走完了
c[k] = a[i];
i++;
} else if (j < b.length) {//或者else if(i >= a.length && j < b.length) 也就是a走完了
c[k] = b[j];
j++;
} else {
throw new RuntimeException("不可能情况");
}
}
for (int k = 0; k < c.length; k++) {
System.out.print(String.valueOf(c[k]) + " ");
}
}
}
(2)无辅助数组,时间复杂度为O(n+m),而空间复杂度为O(1)
思路
刚开始接触题就想到暴力解法,先将B加入到A的后面,然后对组合后的数组进行排序,假设A和B的长度分别是M,N,那么用快排平均也需要nlog(n)的时间复杂度;显然不是一种很好的做法;
第二种想法是再利用一个数组,然后A和B分别比较较小的元素加入到新的数组中,这样时间复杂度是O(M+N),但是空间复杂度又增加了O(M);这种也不是好办法;
第三种想法是在A的左边开始和b比较然后将B插入A中,这种算法移动元素的次数较多;移动一次的时间复杂度O(M),这种算法的时间复杂度是O(M*N);显然也不是好办法;
第四种,也是比较推荐的算法是,先算出A和B的总长度,由于数组中后面的元素是空的,可以从后往前复制,时间复杂度是O(M+N);空间复杂度是O(1);这种想法是比较好的,基于第四种算法的思想做了如下的Java实现
从大的开始比较和”入队“。
比如1 3 5和2 4合并的话,从尾部比较:
5比4大,5放到最后边;
3比4小,4放到5的前面一格;
3比2大,3放到4的前面一格;
1比2小,2放到3的前面一格;
1,没人来比较,1放到2的前面一格。
END
public class SortedArrayCombine2 {
public static void main(String args[]) throws Exception {
int[] source1 = new int[50];
source1[0] = 1;
source1[1] = 2;
source1[2] = 5;
source1[3] = 9;
source1[4] = 13;
int[] source2 = new int[] { 1, 3, 4, 8, 10 };
mergeArray(source1, source2, 5);
// mergeArray(source1, source2, 100);
// mergeArray(source1, null, 5);
// mergeArray(null, source2, 5);
// mergeArray(source1, source2, 4);
}
/**
* @param source1
* @param source2
* m表示数组1已有数据长度这里是5
* n表示数组2已有数据长度
* length=m+n表示合并之后数组中的数据个数
* @return
*/
public static int[] mergeArray(int[] source1, int[] source2, int m) {
/** 先进行判空,如果都非空,长度为0可认为是正确的 */
if (source1 == null || source2 == null) {
System.out.println("there is a null arr");
return null;
}
/** source的长度和已有M不匹配 */
if (m > source1.length) {
System.out.println("the length unmatch with m");
return null;
}
int n = source2.length;
int length = m + n;//两个数组合并之后的新数组中元素的数量
if (source1.length < length) {
System.out.println("数组1的空间不够容纳数组1和2中的元素");
return null;
}
int i = length - 1;
// M+N次运算,上面都是一些特殊数据处理
// 两个数组进行比较,可以不写双重循环,使用两个指针m和n来移动
for (; i >= 0 && m > 0 && n > 0; i--) {
//下面是把source1和source2中相对最大的那个放到合并的数组的后面(i = length - 1)
if (source1[m - 1] > source2[n - 1]) {
source1[i] = source1[m - 1];
m--;
} else {
source1[i] = source2[n - 1];
n--;
}
}
// 比较完剩余的元素能顺利插入
while (m > 0) {
source1[i--] = source1[m--];
}
while (n > 0) {
source1[i--] = source2[n--];
}
for (int count = 0; count < length; count++) {
System.out.print(String.valueOf(source1[count]) + " ");
}
return source1;
}
}