vlambda博客
学习文章列表

[二分查找] 两个等长有序数组的上中位数

 二分查找系列专题   开胃菜

给定两个有序数组arr1和arr2,已知两个数组的⻓度都为N,求两个数组中所有数的上中位数。要求时间复杂度O(logN),空间复杂度O(1)。

举例:

arr1 = [1,2,3,4],arr2 = [3,4,5,6]。
总共8个数,则中位数就是第 4 ⼩的数,为 3.

arr1 = [0,1,2],arr2 = [3,4,5]。
总共6个数,则中位数就是第 3 ⼩的数,为 2.

1 分析

本题限定了时间复杂度为 ,因此用双指针那些方法肯定都是不行的,一般也就二分思想的时间复杂度为 ,因此可以确定要使用二分法。那么应该如何求解呢?

由于两个有序数组的长度是相同的,因此可以想办法利用二分思想在每次的查找过程中待查询的数组的长度减半,递归执行下去直到满足递归结束条件;具体思路(这里参照网上解释较为清楚的题解[1]):

  • 如果每个数组中只有一个元素,较小的那个元素就是整体的上中位数,如果两个元素相等,随便返回哪个都可以。
  • 如果数组中不止一个元素,找到两个数组的中间位置mid1和mid2。
  • 如果arr1[mid1] == arr2[mid2],不管每个数组中元素的个数是奇数还是偶数,这两个数都可以是整体的上中位数,返回其中一个就可以。
  • 如果arr1[mid1] > arr2[mid2],每个数组的个数是奇数的情况下:数组arr1中mid1位置以后的数都不可能是整体的上中位数,数组arr2中mid2位置以前的数都不可能是整体的上中位数。所以现在只需要考虑arr1[left1…mid1]、arr2[mid2…right],这两部分的元素个数相同,它们的上中位数就是整体的上中位数。
  • 如果arr1[mid1] > arr2[mid2],每个数组的个数是偶数的情况下:数组arr1中mid1位置以后的数都不可能是整体的上中位数,数组arr2中mid2位置以后包括mid2位置,都不可能是整体的上中位数。所以现在只需要考虑arr1[left1…mid1]、arr2[mid2+1…right],这两部分的元素个数相同,它们的上中位数就是整体的上中位数。
  • arr1[mid1] < arr2[mid2]的情况,分析同上。

2 代码

#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;

int find(vector<int>& vec1, vector<int>& vec2, int l1, int r1, int l2, int r2) {
if (l1 >= r1) return min(vec1[l1], vec2[l2]);
int mid1 = l1 + (r1 - l1) / 2;
int mid2 = l2 + (r2 - l2) / 2;

int offset = ((r1 - l1 + 1) & 1) ^ 1;
if (vec1[mid1] == vec2[mid2]) {
return vec1[mid1];
}
else if (vec1[mid1] > vec2[mid2]) {
return find(vec1, vec2, l1, mid1, mid2 + offset, r2);
}
else if (vec1[mid1] < vec2[mid2]) {
return find(vec1, vec2, mid1 + offset, r1, l2, mid2);
}
}

int getUpMedian(vector<int>& vec1, vector<int>& vec2) {
if (vec1.size() == 0 || vec2.size() == 0) return -1;
return find(vec1, vec2, 0, vec1.size() - 1, 0, vec2.size() - 1);
}

int main() {
int arr1[] = {1,2,3,4,5}, arr2[] = {2,3,4,5,6};
vector<int> vec1(begin(arr1), end(arr1));
vector<int> vec2(begin(arr2), end(arr2));
cout << getUpMedian(vec1, vec2);
system("pause");
return 0;
}


参考链接

https://blog.csdn.net/qq_34342154/article/details/78462679