leetcode 4.两个排序数组的中位数
题目:
给定两个大小为 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
解题思路:
1.将两个数组合并后排序,算法复杂度取决于排序算法的复杂度,比如使用快速排序,算法复杂度NlogN;
2.思路同1,但不对数组排序,因为两个数组已经排序,只需要合并即可,对两个排序数组合并,算法复杂度可到O(n)(合并只需到n/2处即可停止,但不影响复杂度);
3.使用二分查找法。过程分为两个部分:第一部分是使用二分法查找数组中小于某个值的元素的个数;这个结果结合这个值在另一数组中的位置可标识其在整个排序数组中的位置;第二部分则是使用二分法和第一部分的方法遍历数组查找中位数;
代码如下:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) { int c1 = nums1.size(); int c2 = nums2.size(); bool odd = (c1 + c2) % 2 == 1; int m1 = FindNth(nums1,nums2,(c1 + c2) / 2); if(!odd) { int m2 = FindNth(nums1,nums2,(c1 + c2) / 2 - 1); return (m1 + m2) / 2.0; } else { return m1; } } int FindNth(vector<int>& nums1, vector<int>& nums2, int n) { if(nums1.empty() && nums2.empty()) { return 0; } int left = 0; int right = nums1.size() - 1; while(left <= right) { int md = left + (right - left) / 2; int r = GetLess(nums2,nums1[md]); if(md + r > n) { right = md-1; } else if(md + r < n) { left = md + 1; } else { return nums1[md]; } } left = 0; right = nums2.size() - 1; while(left <= right) { int md = left + (right - left) / 2; int r = GetLess(nums1,nums2[md]); if(md + r > n) { right = md-1; } else if(md + r < n) { left = md + 1; } else { return nums2[md]; } } return nums2[left]; //针对两个数组中有相同元素的情况, } int GetLess(vector<int>& nums, int n) { int left =0; int right = nums.size()-1; while(left <= right) { int mid = left + (right - left) / 2; if(nums[mid] < n) { left = mid+1; } else if(nums[mid] > n) { right = mid-1; } else { return mid + 1; } } return left; }
这个解法中需要注意的是在两个数组中有相同元素的情况(一个数组有相同元素不会有问题),比如nums1={1},nums2={1},这种情况下,根据我们使用“当前数组的位置+另一个数组中小于该元素的个数”的方法,则会出现两个相同元素在总数组中位置相同的情况。我们可以这么理解,假设V在总数组中的位置是p,其相同值的另一个元素位置则是p-1,但根据我们的计算方式这两个元素计算的位置结果是一样的,都是p,也就是说我们使用FindNth(nums,p-1)的时候,两个while循环不会返回,即找不到p-1的元素,所以在FindNth中,我们直接返回left指向的指,注意这时left肯定指向相同的元素。