给定两个大小为 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
解题思路:
二分法,求第K大的数。当两个数组总个数为奇数时,中位数是第K=(nums1.size()+nums2.size()+1)/2 大的数;当两个数组总个数为偶数时,需要第K大的数与第K+1大的数二者取平均,其中K=(nums1.size()+nums2.size())/2,于是算法的核心就转换成如何求解第K大的数,时间复杂度为log(n+m)。通过程序流程可以很容易得理解算法的主要思想。
例子nums1=[1,2,3,4,5,6,7,8,9,10];
nums2=[3,4,6,7,11,12];
求解第K=7大的数,K<=nums1.size()+nums2.size()=16。
1. 根据K值缩小查找范围。取两个数组的前K个,如果当前数组不够那就全取。此刻nums1=[1,2,3,4,5,6,7];nums2=[3,4,6,7,11,12];这样一来第K=7大的数就必然在这之间。
2. 为了方便处理,通常设置nums1的长度大,如果不是那就交换nums1,nums2;
3. nums1的长度可能不足K个,需要填充最大值来确保nums1的长度为K,此刻并不影响第K大的数,只是为了方便处理。
4. 在确保nums1的长度为K之后,将pos1=k-2=5,nums1[pos1]=6;pos2=0,nums[pos2]=3;此刻我们选取了K个数。
5.我们发现3后面的数小于6,所以pos1,pos2不是最终位置,因此需要迭代,理论上pos2(更小的那个)一直往后加一,同时pos1(更大的那个)往前减一,就必然能找到那个结果,但是时间复杂度为m+n。
6. 利用二分。其实每一次的移动量为1是不必要的,应该设置为可移动区间的中间位置,区间为闭区间[left,right]。
7.初始时刻pos1的访问区间,board_max=[0,pos1],board_min=[pos2,nums2.size()-1]。
8. pos2往后移动move = (board.right-board.left)/2 = 2。与此同时,pos1往前移动move。修改区间,borad_max=[left,right-move]=[0,3],board_min=[left+move,right]=[2,5]。
9. 查看当前位置是否需要换边。nums1[3]=4,nums2[2]=6。这个时候,更大的数值和更小的数值换了边,之前第4步,更大在nums1,而现在到了nums2。有换边情况也是需要修改移动区间,因为他们的移动方向会发生改变,这是本次刷题遇到的主要错误。board_max=[right-move,pre_max]=[3,5];board_min=[pre_min,left+move];swap(board_max,board_min);其中pre_max是指针往前移动之前的位置,pre_min是指针往后移动之前的位置!!!
10. 考虑移动区间的边界之后,再次考虑迭代条件。这个时候访问到nums1[3]=4,nums2[2]=6,4后面的数值为5<6所以迭代。迭代条件是更小的数后面一个数小于更大的那个数。移动量move=1。访问nums1[4]=5,nums2[1]=4。此刻又发生了换边,但是不满足迭代条件,因为4后面的数6<5。此刻return max=5即可。
11. 解决了查找第K个数的问题之后查找中位数简直so easy!
class Solution { public: double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) { int size1 = nums1.size(), size2 = nums2.size(); if (size1 == 0) { if (size2 % 2 == 1) return nums2[size2 / 2]; else return double(nums2[size2 / 2 - 1] + nums2[size2 / 2]) / 2; } else if (size2 == 0) { if (size1 % 2 == 1) return nums1[size1 / 2]; else return double(nums1[size1 / 2 - 1] + nums1[size1 / 2]) / 2; } else { if ((size1 + size2) % 2 == 1) return findKth(nums1, nums2, (size1 + size2 + 1) / 2); else return (findKth(nums1, nums2, (size1 + size2) / 2 ) + findKth(nums1, nums2, (size1 + size2) / 2+1)) / 2; } } double findKth(vector<int> nums1, vector<int> nums2, int k) {//在两个数组中找第k大的数,数组已排序。 if (nums1.size() < nums2.size()) swap(nums1, nums2); vector<int>::iterator it_max; vector<int>::iterator it_min; int size1 = nums1.size(), size2 = nums2.size(); int max = (nums1[size1 - 1] > nums2[size2 - 1] ? nums1[size1 - 1] : nums2[size2 - 1]); if (int(nums1.size()) <= k) { int n = k - int(nums1.size()); while (n--) { nums1.push_back(max); size1++; } } if (int(nums2.size() <= k)&& int(nums1.size())>k) { nums1.erase(nums1.begin() + k, nums1.end());} else { if (int(nums1.size()) > k) nums1.erase(nums1.begin() + k, nums1.end()); if (int(nums2.size()) > k) nums2.erase(nums2.begin() + k, nums2.end()); } if (nums1[k - 2] == nums2[0]) return nums1[k - 2]; int pos_max, pos_min, max_size, min_size; if (nums1[k - 2] > nums2[0]) { it_max = nums1.begin(); it_min = nums2.begin(); pos_max = k - 2; pos_min = 0; max_size = nums1.size(); min_size = size2; } else { it_max = nums2.begin(); it_min = nums1.begin(); pos_max = 0; pos_min = k - 2; max_size = size2; min_size = nums1.size(); } pair<int, int> board_max(0, pos_max), board_min(pos_min, min_size - 1); int sgn1, move, pre_min, pre_max; while ((pos_min<board_min.second&&pos_min>=board_min.first)&&it_min[pos_min + 1] < it_max[pos_max]) {//迭代条件 sgn1 = board_min.second - pos_min; move = (sgn1 % 2 == 0 ? sgn1 / 2 : sgn1 / 2 + 1); pre_min = pos_min; pre_max = pos_max; pos_min = pos_min + move; board_min.first = pos_min; pos_max = pos_max - move; board_max.second = pos_max; //检测是否有必要交换指针 if (pos_max<0||(pos_max>=0&&it_max[pos_max] < it_min[pos_min])) { swap(it_max, it_min); swap(pos_max, pos_min); swap(max_size, min_size); swap(board_min, board_max); board_min.first = board_min.second; board_min.second = pre_max; board_max.second = board_max.first; board_max.first = pre_min; } } return it_max[pos_max]; } }; |