前言
在工作中我们会经常使用数组这个数据结构,其中会碰到需要合并数组的且使之有序的需求,一般经过查询(数据库排序)返回的结果大多数都是有序数组,那么如何将多个数组进行合并呢?
其实无论是两个还是多个数组,本质上都可以利用数组的两两合并。
实现方式
排序法
我们大可以直接直接合并两个或者多个数组,不用考虑其顺序,然后再使用排序即可,在Java里甚至可以使用集合的addAll和sort方法,仅需两行代码就可以搞定,接下来让我们分析一下,假设总元素个数为n且使用快速排序,那么需要O(nlogn)的时间复杂度,以及快排所需的O(logn)空间复杂度。
移位法
如果数组1足够长,为了不利用新的数组空间,我们可以从头开始遍历两个数组将小的插入到数组1中并将数组1中的所有元素后移一位即可
public void merge(int[] nums1, int m, int[] nums2, int n) {
int size = m ,i = 0, j = 0;
for(i = 0,j = 0; i<size&&j<n; i++){
if(nums1[i]>nums2[j]){
size++;
for(int k = size-1; k>i; k--){
nums1[k]=nums1[k-1];
}
nums1[i] = nums2[j];
j++;
}
}
for(int k = j; k<n; k++){
nums1[m+k] = nums2[k];
}
}
最坏的时间复杂度为O(n^2),空间复杂度为O(1)
双指针
排序法没有利用数组有序这一特性,我们可以考虑新建一个长度为n的数组,然后将数组1和数组2,分别从头开始比较,元素较小的进入新数组,并是原数组的索引加1,直至最后,再将剩余的元素直接放入新数组尾部即可,代码如下:
public void merge(int[] nums1, int m, int[] nums2, int n) {
int index1 = 0, index2 = 0;
int[] sorted = new int[m + n];
int min;
while (index1 < m || index1 < n) {
if (p1 >= m) {
min= nums2[index2++];
} else if (p2 >= n) {
min= nums1[index1++];
} else if (nums1[index1] < nums2[index2]) {
min= nums1[index1++];
} else {
min= nums2[index2++];
}
sorted[index1 + index2 - 1] = cur;
}
}
当某个数组达到边界时,我们可以认为其实无穷大的,这样另外一个数组剩余的元素就会自动进入新数组,时间和空间复杂度均为O(n)
逆向双指针
上述方式我们利用数组是有序的特点,但是如果给出的数组1本身可以容纳所有元素时,我们不妨从两个数组的尾部进行比较,假设num1含有m元素,num2含有n个元素,且num1数组的容量是m+n时,我们可以直接将合并后的元素放到num1中而不是新建一个长度为n的数组,此时我们只需要避免在插入到num1时避免将其本来的元素覆盖即可,这个时候就可以利用num1尾部是空的特性,分别从两个元素的最后一个元素开始比较,将较大的元素放到num1的尾部就可以实现
public void merge(int[] nums1, int m, int[] nums2, int n) {
int index1 = m - 1, index2 = n - 1;
int tail = m + n - 1;
int max;
while (index1>= 0 || index2>= 0) {
if (index1<= -1) {
max= nums2[index2--];
} else if (index2<= -1) {
max= nums1[index1--];
} else if (nums1[index1] > nums2[index2]) {
max= nums1[index1--];
} else {
max= nums2[index2--];
}
nums1[tail--] = max;
}
}
同理,当索引为-1时说明该数组已经遍历结束,为了将另外一个数组的元素继续放到nums1中,我们认为遍历结束的数组为无穷小即可,此时空间复杂度为O(1),时间为O(m+n)
结束语
不定期更新算法结题方法,有更好的方法欢迎指导~