将两个升序数组合并成一个新的升序数组
晚上突然接到朋友一个灵魂拷问,如何把将两个升序数组 a[] 和 b[] 合并成一个升序数组 c[]
第一反应就是把两个数组按位拷贝到一个新的数组中,再排序不就完事了嘛,要什么效率能跑就行。但是那一天,我终于回想起曾经一度被面试官支配的恐惧,以及还在做码农搬砖的屈辱。我想起了写下第一行代码的时候,天空是那么蓝,理想是那么丰满,啊,不好意思扯远了。
其实这个题目的解法思路非常简单,但是设计起来就很费力。
我的基本思路就是创建一个新的数组,通过‘对撞指针’(我开始并不知道,我只知道从两端同时处理会更高效一点)对数组进行遍历式的存放,即两个源数组从头部(index为0)和尾部(index为数组长度-1)同时进行比较,比较的结果存放到目标数组对应的下标位置中,同时移动对应源数组的指针和目标数组的指针,直到目标数组左右指针交叉,计算完成。
我的代码实现如下,特此记录,以免将来自己都忘记了当初自己是怎么做的,毕竟灵感就像是放屁一样,指不定什么时候就蹦出来一个
i1: 数组arr1的头部指针
k1: 数组arr1的尾部指针
i2: 数组arr2的头部指针
k2: 数组arr2的尾部指针
i3: 目标数组arr3的头部指针
k3:目标数组arr3的尾部指针
m1: 一个标记,从前到后放入到目标数组中的元素是否是arr1中的元素,如果是,则表示要移动arr1的指针,否则移动arr2的,index+1
m2: 一个标记,从后往前放入目标数组中的元素是否是arr1中的元素,如果是,则表示要移动arr1的指针,否则移动arr2的,index-1
上面有个bug,两个数组中的某一个中,分布的元素数值过于大或者过于小,且元素个数很小,会让我们设置的指针一直单向偏移,最终造成数组下标越界的风险。如果左右指针只要有一个达到数组的边界,就说明这个数组已经全部合并到目标数组中去了,接下来就只要把空间让另一个源数组的剩余元素填充就行了。2021-07-30晚上更新如下:
private static int[] simpleMerge(int[] arr1, int[] arr2) { int n1 = arr1.length; int n2 = arr2.length; int n3 = n1 + n2; int[] arr3 = new int[n1 + n2]; boolean m1, m2; int i1 = 0, i2 = 0, i3 = 0, k1 = n1 - 1, k2 = n2 - 1, k3 = n3 - 1; while (true) { if (i3 > k3) { break; } if (i1 > n1 - 1 || k1 < 0) { arr3[i3] = arr2[i2]; arr3[k3] = arr2[k2]; m1 = false; m2 = false; } else if (i2 > n2 - 1 || k2 < 0) { arr3[i3] = arr1[i1]; arr3[k3] = arr1[k1]; m1 = true; m2 = true; } else { m1 = arr1[i1] > arr2[i2] ? (arr3[i3] = arr2[i2]) == arr1[i1] : (arr3[i3] = arr1[i1]) == arr1[i1]; m2 = arr1[k1] > arr2[k2] ? (arr3[k3] = arr1[k1]) == arr1[k1] : (arr3[k3] = arr2[k2]) == arr1[k1]; } if (m1) { i1++; } else { i2++; } if (m2) { k1--; } else { k2--; } i3++; k3--; } return arr3; }