题目描述:
给定两个大小为 m 和 n 的有序数组 nums1 和 nums2。
请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。
你可以假设 nums1 和 nums2 不会同时为空。
示例 1:
nums1 = [1, 3]
nums2 = [2]
则中位数是 2.0
示例 2:
nums1 = [1, 2]
nums2 = [3, 4]
则中位数是 (2 + 3)/2 = 2.5
解题思路:
刚拿到这道题的时候就看到时间复杂度为O(log(m + n)),这说明最容易想到的合并加排序肯定不行。随后想到合并之后不排序也可以找到中位数,《算法导论》中有这个方法,但时间复杂度为O(n)。按顺序插入也不行,因在vector里面插入是很费时的,时间复杂度为O(m*n)。
其实时间复杂度中有log的时候一般都用到了分治的思想,比如二分查找就是典型的分治。顺着这个思路很容易想到,每次取两个数组的中位数进行比较,然后根据比较的结果再做下一步。
我们假设两个数组可以表示成
X1 a X2
Y1 b Y2
其中a, b分别为两个数组的中位数。XY代表数组中被中位数分割的部分。
那么有X1 < a < X2, Y1 < b < Y2。如果a < b,则有X1 < b, a < Y2。如果a > b, 则有b < X2, Y1 < a。
有了这些准备,我们可以看出,假设a < b,则有X1小于a和b,Y2大于a和b。更进一步,X1比a和X2小,也比b和Y2小,所以X1中一定没有中位数,X1中的元素要比中位数小,同理可以的到Y2比中位数大。那么如果我们舍弃X1和Y2中相同数量的元素,则缩小了问题的规模,但问题的性质不变。因此我们每次都舍弃X1和Y2中较小的那一个值的元素,并递归调用findMedianSortedArrays()。a > b时,交换数组x和y则回到了a < b的情况。
整体思路有了,接下来就是特判了。
有一个难点就是当数组为偶数的时候中位数会分为上中位数和下中位数,我在代码中统一使用下中位数。但是这样就有一个情况,那就是当两个数组都是偶数个的时候,X1中可能会包含总中位数。而如果包含那么一定是X数组中位数的上一个数,所以我们舍弃X1的时候一定要主要判断,如果X的中位数的上一个数的值(即X1的最后一个元素)大于Y1中的所有值,那么一定不能把X1的最后一个元素给舍弃了,我的解决方法是把X1的最后一个元素和Y1的最后一个元素交换位置,然后保持原进程不变。
剩下的一个特殊情况就是当a等于b的时候,这个时候如果X和Y只要不都是偶数个元素,那么直接返回a或b就行了,原因大家可以自己想想。那为什么偶数个不行呢?原因其实和上面的难点一样,我们同时取下中位数的时候,X1中可能包含了总的中位数,所以这时需要特判。
最后就是边界条件的特判了,这个大家慢慢调试就行了。
下面给出我的代码:
class Solution { public: inline double divide(int a, int b) { return a - (a - b) / 2.0; } double findMedianOfArray(vector<int>& ary) { if (ary.size() % 2) return ary[ary.size() / 2]; else return divide(ary[(ary.size()/2) - 1], ary[ary.size()/2]); } double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) { for (auto i : nums1) cout << i << ' '; cout << endl; for (auto i : nums2) cout << i << ' '; cout << endl << endl; int median1, median2; if (!nums1.empty()) median1 = nums1[nums1.size()/2]; else return findMedianOfArray(nums2); if (!nums2.empty()) median2 = nums2[nums2.size()/2]; else return findMedianOfArray(nums1); if (nums1.size() == 1 && nums2.size() == 1) { return divide(nums1[0], nums2[0]); } if (median1 == median2) { if (nums1.size()%2 == 0 && nums2.size()%2 == 0) { int num = nums1[nums1.size()/2 - 1] > nums2[nums2.size()/2 - 1] ? nums1[nums1.size()/2 - 1] : nums2[nums2.size()/2 - 1]; return divide(num, median1); } return median1; } else if (median1 < median2) { int left1 = nums1.size()/2; int right2 = nums2.size() - nums2.size()/2 - 1; if (left1 == 0) { //nums1只有一个元素 int nums2_before = nums2[nums2.size()/2 - 1]; if (median1 > nums2_before) { if (nums2.size() % 2) //基数个 return divide(median1, median2); else return median1; } else { if (nums2.size() % 2) //基数个 return divide(nums2_before, median2); else return nums2_before; } } if (right2 == 0) { for (auto i = nums2.begin(); i != nums2.end(); ++i) nums1.push_back(*i); sort(nums1.begin(), nums1.end()); return findMedianOfArray(nums1); } if (nums1.size()%2 == 0 && nums2.size()%2 == 0) { //如果都是偶数且n1中位数的前一个数比n2中位数前所有元素都大,则不能移除n1中位数前面的数 int nums1_before = nums1[nums1.size()/2 - 1]; int nums2_before = nums2[nums2.size()/2 - 1]; if (nums1_before > nums2_before) swap(nums1[nums1.size()/2 - 1], nums2[nums2.size()/2 - 1]); } if (left1 > right2) { vector<int> new1(nums1.begin()+right2, nums1.end()); vector<int> new2(nums2.begin(), nums2.end()-right2); return findMedianSortedArrays(new1, new2); } else { vector<int> new1(nums1.begin()+left1, nums1.end()); vector<int> new2(nums2.begin(), nums2.end()-left1); return findMedianSortedArrays(new1, new2); } } else return findMedianSortedArrays(nums2, nums1); } };