给定两个递增数组arr1和arr2,已知两个数组的长度都为N,求两个数组中所有数的上中位数。
上中位数:假设递增序列长度为n,为第n/2个数
数据范围:,
要求:时间复杂度 ,空间复杂度
进阶:时间复杂度为,空间复杂度为
[1,2,3,4],[3,4,5,6]
3
总共有8个数,上中位数是第4小的数,所以返回3。
[0,1,2],[3,4,5]
2
总共有6个数,那么上中位数是第3小的数,所以返回2
[1],[2]
1
int findMedianinTwoSortedAray(vector<int>& arr1, vector<int>& arr2) { // write code here if(arr1.size()==0 || arr2.size()==0) return -1; return recurseFindMedian(arr1, arr2, 0, arr1.size()-1, 0, arr2.size()-1); } int recurseFindMedian(vector<int>& arr1, vector<int>& arr2, int s1, int e1, int s2, int e2) { if(s1==e1 && s2==e2) return min(arr1[s1], arr2[s2]); int mid1 = (e1+s1)/2; int mid2 = (e2+s2)/2; if(arr1[mid1] == arr2[mid2]) return arr1[mid1]; return arr1[mid1] < arr2[mid2] ? recurseFindMedian(arr1, arr2, e1-mid2+s2, e1, s2, mid2) : recurseFindMedian(arr1, arr2, s1, mid1, e2-mid1+s1, e2); }
public static int findMedianinTwoSortedAray(int[] nums1, int[] nums2) { /** * 设nums1长度为m,nums2长度为n,设m <= n * 将nums1与nums2分别进行分组,设nums1的左边长度为i,则右边长度 m - i;设nums2的左边长度为j,则右边长度为 n - j * 如果m + n 为偶数,则有 i + j = m - i + n - j,如果 m + n 是奇数,则有 i + j = m - i + n - j + 1 * 因此有 j = (m + n + 1)/2 - i,(因为只取商,所以 m + n 的和无论是偶数还是奇数都满足这个结果) * 还有 nums1[i - 1] <= nums2[j] 且 nums1[i] >= nums2[j - 1] * * 又因为 i ∈ [0, m],如果需要同时满足条件( nums1[i - 1] <= nums2[j] 且 nums1[i] >= nums2[j - 1]) * 所以可以取最大的 i 使其满足nums1[i - 1] <= nums2[j]即可 * */ if (nums1.length > nums2.length) { return findMedianinTwoSortedAray(nums2, nums1); } int len1 = nums1.length; int len2 = nums2.length; int start = 0; int end = len1; int median1 = 0; while (start <= end) { int mid = (start + end) / 2; int nums2Index = (len1 + len2 + 1) / 2 - mid; int nums1Left = mid == 0 ? Integer.MIN_VALUE : nums1[mid - 1]; int nums1Right = mid == len1 ? Integer.MAX_VALUE : nums1[mid]; int nums2Left = nums2Index == 0 ? Integer.MIN_VALUE : nums2[nums2Index - 1]; int nums2Right = nums2Index == len2 ? Integer.MAX_VALUE : nums2[nums2Index]; if (nums1Left <= nums2Right) { median1 = Math.max(nums1Left, nums2Left); start = mid + 1; } else { end = mid - 1; } } return median1; }
class Solution { public: /** * find median in two sorted array * @param arr1 int整型vector the array1 * @param arr2 int整型vector the array2 * @return int整型 */ int findMedianinTwoSortedAray(vector<int>& arr1, vector<int>& arr2) { // write code here int n = arr1.size(); int left = 0, right = n-1; // binary search [0,n-1) while(left < right) { int m1 = left + ((right - left) >> 1); // int m2 = n - m1 - 2; if(arr1[m1] > arr2[m2+1]) { right = m1; }else if(arr2[m2] > arr1[m1+1]) { left = m1 + 1; }else if(arr1[m1] <= arr2[m2+1] && arr2[m2] <= arr1[m1+1]) { return max(arr1[m1], arr2[m2]); } } if(left == n-1) return arr1[n-1]; if(right == 0) return arr2[n-1]; } };
class Solution { public: /** * find median in two sorted array * @param arr1 int整型vector the array1 * @param arr2 int整型vector the array2 * @return int整型 */ int findMedianinTwoSortedAray(vector<int>& arr1, vector<int>& arr2) { int m = arr1.size(); return getKmin(arr1, 0, m-1, arr2, 0, m-1, m); } int getKmin(vector<int>& nums1, int l1, int r1, vector<int>& nums2, int l2, int r2, int k){ int len1 = r1-l1+1; int len2 = r2-l2+1; if(len1 == 0){ return nums2[l2+k-1]; } if(len2 == 0){ return nums1[l1+k-1]; } if(k == 1){ return min(nums1[l1], nums2[l2]); } int nl1 = l1 + min(len1, k/2) - 1; int nl2 = l2 + min(len2, k/2) - 1; if(nums1[nl1] <= nums2[nl2]){ return getKmin(nums1, nl1+1, r1, nums2, l2, r2, k - (nl1-l1+1)); } else{ return getKmin(nums1, l1, r1, nums2, nl2+1, r2, k - (nl2-l2+1)); } } };
int findMedianinTwoSortedAray(vector<int>& arr1, vector<int>& arr2) { // write code here int n = arr1.size() - 1; int l1 = 0,h1 = n,l2 = 0,h2 = n,m1,m2; while(l1 <= h1 && l2 <= h2) { m1 = (l1 + h1)/2; m2 = (l2 + h2)/2; if (m1 + m2 == n) { if (arr1[m1] == arr2[m2]) return arr1[m1]; else if (arr1[m1] < arr2[m2]) { if (m2 == 0 || (m2 > 0 && arr1[m1] >= arr2[m2-1])) return arr1[m1]; else l1 = m1 + 1; } else { if (m1 == 0 || (m1 > 0 && arr2[m2] >= arr1[m1-1])) return arr2[m2]; else l2 = m2 + 1; } } else if (m1 + m2 < n) { if (arr1[m1] < arr2[m2]) l1 = m1 + 1; else l2 = m2 + 1; } else { if (arr1[m1] > arr2[m2]) h1 = m1 - 1; else h2 = m2 - 1; } } return 0; }
import java.util.*; public class Solution { public int findMedianinTwoSortedAray (int[] arr1, int[] arr2) { int m = arr1.length, n = arr2.length; if ((m + n) % 2 == 1) { return findK(arr1, arr2, 0, m - 1, 0, n - 1, (m + n) / 2 + 1); } else { return findK(arr1, arr2, 0, m - 1, 0, n - 1, (m + n) / 2); } } //在两个有序数组中找第 K 个 private int findK(int[] a1, int[] a2, int l1, int r1, int l2, int r2, int k) { //其中一方越界 if (l1 > r1) return a2[l2 + k - 1]; if (l2 > r2) return a1[l1 + k - 1]; //注意基本情况 k == 1 if (k == 1) return Math.min(a1[l1], a2[l2]); int ind1, len1, ind2, len2; //优先考虑长度比较短的一方,正常来说应该要取 k / 2 个数,但是要考虑长度不够的情况 //另一方取的就是 k - 已经取了的 if (r1 - l1 <= r2 - l2) { len1 = Math.min(k / 2, r1 - l1 + 1); len2 = k - len1; } else { len2 = Math.min(k / 2, r2 - l2 + 1); len1 = k - len2; } ind1 = l1 + len1 - 1; ind2 = l2 + len2 - 1; //更新范围。小的一方后半段 + 大的一方前半段。 //更新k。小的一方前半段肯定在前 k 个的范围,减掉找剩下的 if (a1[ind1] <= a2[ind2]) { return findK(a1, a2, ind1 + 1, r1, l2, ind2, k - len1); } else { return findK(a1, a2, l1, r1, ind2 + 1, r2, k - len2); } } }
public int findMedianinTwoSortedAray (int[] arr1, int[] arr2) { //和leetcode的 两个有序数组的中位数 很像。 int l=0,r=arr1.length,mid=0; int n=arr1.length; if (n==0) return 0; int j=0; while (l<=r){ mid=(l+r)/2;//往arr1中取出mid个数 j=n-mid;//往arr2中取出多少个数 int arr1_left=mid==0?Integer.MIN_VALUE:arr1[mid-1]; int arr1_right=mid==n?Integer.MAX_VALUE:arr1[mid]; int arr2_left=j==0?Integer.MIN_VALUE:arr2[j-1]; int arr2_right=j==n?Integer.MAX_VALUE:arr2[j]; int left_max=Math.max(arr1_left,arr2_left); int right_min=Math.min(arr1_right,arr2_right); if (left_max<right_min) return left_max; else if (arr1_left<arr2_left){ l=mid+1; }else r=mid; } return 0; }
class Solution { public: vector<int> arr1; vector<int> arr2; int findMedianinTwoSortedAray(vector<int>& arr1, vector<int>& arr2) { int n1 = arr1.size(); int n2 = arr2.size(); this->arr1.swap(arr1); this->arr2.swap(arr2); return findKthMin(0, n1, 0, n2, (n1 + n2 + 1) / 2); } int findKthMin(int b1, int e1, int b2, int e2, int k) { if(b1 == e1) return arr2[b2 + k - 1]; if(b2 == e2) return arr1[b1 + k - 1]; if(k == 1) return min(arr1[b1], arr2[b2]); int m = min({k / 2, e1 - b1, e2 - b2}); if(arr1[b1 + m - 1] < arr2[b2 + m - 1]) return findKthMin(b1 + m, e1, b2, e2, k - m); else return findKthMin(b1, e1, b2 + m, e2, k - m); } };
class Solution { public: /** * find median in two sorted array * @param arr1 int整型vector the array1 * @param arr2 int整型vector the array2 * @return int整型 */ int findMedianinTwoSortedAray(vector<int>& arr1, vector<int>& arr2) { // write code here //题目出的有漏洞,两个长度都为N的数组,合并后长度为2N,肯定为偶数 //按照题目中上中位数定义:假设递增序列长度为n,若n为奇数,则上中位数为第n/2+1个数;否则为第n个数 //只需要输出两个数组中最大的数就行了,偶数时上中位数应为第n/2个数 if(arr1.size()==0 && arr2.size() == 0) { return 0; } //如果有一个是空数组,返回另外一个数组的上中位数 if(arr1.size() == 0) { return midvalue(arr2); } if(arr2.size() == 0) { return midvalue(arr1); } int len = arr1.size()+arr2.size(); //计算上中位数坐标 int midindex = len/2; if(len%2 == 0) { midindex -= 1; } int index1 = 0; int step1 = 1; int limit1 = arr1.size(); int min1 = arr1[0]; int max1 = arr1[arr1.size()-1]; int index2 = 0; int step2 = 1; int limit2 = arr2.size(); int min2 = arr2[0]; int max2 = arr2[arr2.size()-1]; if(min1 > max1)//数组1为降序,从尾部开始遍历 { index1 = arr1.size()-1; step1 = -1; limit1 = 0; min1 = arr1[arr1.size()-1]; max1 = arr1[0]; } if(min2 > max2)//数组2为降序,从尾部开始遍历 { index2 = arr2.size()-1; step2 = -1; limit2 = 0; min2 = arr2[arr2.size()-1]; max2 = arr2[0]; } //优化增加边界判断,如果一个数组的最小值大于等于另外一个数组的最大值,可以直接通过坐标值取得结果 if(min2 >= max1) { if(midindex < arr1.size()) { if(step1 == 1) { return arr1[midindex]; } else { return arr1[arr1.size() - 1 - midindex]; } } else { if(step2 == 1) { return arr2[midindex - arr1.size()]; } else { return arr2[arr2.size() - 1 - midindex + arr1.size()]; } } } if(min1 >= max2) { if(midindex < arr2.size()) { if(step2 == 1) { return arr2[midindex]; } else { return arr2[arr2.size() - 1 - midindex]; } } else { if(step1 == 1) { return arr1[midindex - arr2.size()]; } else { return arr1[arr1.size() - 1 - midindex + arr2.size()]; } } } for(int i=0;i<=midindex;i++) { if(i == midindex) { if(index1 != limit1 && index2 != limit2) { if(arr1[index1] < arr2[index2]) { return arr1[index1]; } else { return arr2[index2]; } } else if(index1 != limit1) { return arr1[index1]; } else if(index2 != limit2) { return arr2[index2]; } } else { if(index1 != limit1 && index2 != limit2) { if(arr1[index1] < arr2[index2]) { index1 += step1; } else { index2 += step2; } } else if(index1 != limit1) { index1 += step1; } else if(index2 != limit2) { index1 += step1; } } } return 0; } int midvalue(vector<int>& arr) { int size = arr.size(); if(size%2 == 0) { if(arr[0] < arr[size-1]) { return arr[size/2 - 1]; } else { return arr[size/2]; } } else { return arr[size/2]; } } };
看到时间复杂度为O(logN),很容易想到二分查找。过程如下:
如果每个数组中只有一个元素,较小的那个元素就是整体的上中位数,如果两个元素相等,随便返回哪个都可以。
如果数组中不止一个元素,找到两个数组的中间位置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]的情况,分析同上。
class Solution { public: /** * find median in two sorted array * @param arr1 int整型vector the array1 * @param arr2 int整型vector the array2 * @return int整型 */ //由时间复杂度,想到二分查找 int findMedianinTwoSortedAray(vector<int>& arr1, vector<int>& arr2) { // write code here int n = arr1.size(); if(n==0){ return NULL; } //arr1左右两端 int l1=0,r1=n-1; //arr2左右两端 int l2=0,r2=n-1; int mid1,mid2; //终止条件为l1=r1,即两个数组都只有一个元素,此时的上中位数为两数的最小值 while(l1< r1){ //arr1中位数 mid1 = l1+((r1-l1)>>1); //arr2中位数 mid2 = l2+((r2-l2)>>1); int k = r1-l1+1; if(arr1[mid1] == arr2[mid2]){ //若两数组中位数相等,整体中位数也是这个 return arr1[mid1]; } else if(arr1[mid1] > arr2[mid2]){ if(k%2 == 0){//区间元素个数为偶数 r1 = mid1; //整体中位数在arr1左区间,包括mid1 l2 = mid2+1; //整体中位数在arr2右区间,不包括mid2 } else if(k%2 == 1){ //区间元素个数为奇数 r1 = mid1; //整体中位数在arr1左区间,包括mid1 l2 = mid2; //整体中位数在arr2右区间,包括mid2 } } else if (arr1[mid1] < arr2[mid2]){ if(k%2 == 0){//区间元素个数为偶数 r2 = mid2; //整体中位数在arr2左区间,包括mid2 l1 = mid1+1; //整体中位数在arr1右区间,不包括mid1 } else if(k%2 == 1){ //区间元素个数为奇数 r2 = mid2; //整体中位数在arr2左区间,包括mid2 l1 = mid1; //整体中位数在arr1右区间,包括mid1 } } } //当区间内只有一个元素时,两个区间中最小值即为整体中位数 return min(arr1[l1],arr2[l2]); } };
class Solution { public: /** * find median in two sorted array * @param arr1 int整型vector the array1 * @param arr2 int整型vector the array2 * @return int整型 */ int findMedianinTwoSortedAray(vector<int>& arr1, vector<int>& arr2) { // write code here int l = 0, r = 0; int res = 0; while(l + r < arr1.size()){ if(arr2[r] < arr1[l]) res = arr2[r++]; else res = arr1[l++]; } return res; } };
class Solution { public: /** * find median in two sorted array * @param arr1 int整型vector the array1 * @param arr2 int整型vector the array2 * @return int整型 */ int findMedianinTwoSortedAray(vector<int>& arr1, vector<int>& arr2) { // write code here int N = arr1.size(); int l = 0, r = N-1; int res = 0; while(l <= r){ int mid = (l+r)>>1; if(mid == N-1) return min(arr1[N-1], arr2[0]); if(arr1[mid] == arr2[N-mid-2]) return arr1[mid]; if(arr1[mid] < arr2[N-mid-2]){ l = mid+1; } else r = mid-1; } if(r < 0) return min(arr1[0], arr2[N-1]); int a = max(arr1[l], arr2[N-2-l]); int b = max(arr1[r], arr2[N-2-r]); return min(a, b); } };
public int findMedianinTwoSortedAray (int[] arr1, int[] arr2) { int len = arr1.length; int a1 = 0; int a2 = 0; while(len>1){ if(arr1[a1]<=arr2[a2])a1++; else a2++; len--; } return Math.min(arr1[a1],arr2[a2]); } 不需要想的太复杂,既然题目说了是有序数组,那就双指针分别指向两个数组,按大小顺序 移动,移动次数为数组长度(题目中说的偶数奇数情况根本没用,因为两个数组长度相等, 不可能得到奇数个数字,中位数必然是第arr1.length个),指针移完了就取两个数组中小的。
import java.util.*; public class Solution { /** * find median in two sorted array * @param arr1 int整型一维数组 the array1 * @param arr2 int整型一维数组 the array2 * @return int整型 */ public int findMedianinTwoSortedAray (int[] arr1, int[] arr2) { // write code here int n = arr1.length; int l = 0, r = 0,res = 0; while(l + r < n) { if(arr1[l] < arr2[r]) res = arr1[l++]; else res = arr2[r++]; } return res; } }
class Solution { public: /** * find median in two sorted array * @param arr1 int整型vector the array1 * @param arr2 int整型vector the array2 * @return int整型 */ int findMedianinTwoSortedAray(vector<int>& arr1, vector<int>& arr2) { int l1 = 0, r1 = arr1.size() - 1, l2 = 0, r2 = arr2.size() - 1; while(l1 < r1){ int mid1 = (l1 + r1) >> 1, mid2 = (l2 + r2) >> 1; //判断子区间的个数是奇数还是偶数 0代表是奇数(奇数中间的数字不能删除) 1代表是偶数 int even = (r1 - l1) % 2; if(arr1[mid1] == arr2[mid2]){ return arr1[mid1]; } else if (arr1[mid1] > arr2[mid2]){ //证明arr1前面的子数组没用 arr2后面的子数组没用 // r1 = mid1 + even; //如果是奇数就是啥都不做 /* 3456 1234 得取34所有 l2 应该向后面取一位 */ r1 = mid1; l2 = mid2 + even; } else { l1 = mid1 + even; //如果是偶数 就是它的后一位 r2 = mid2; } } //如果俩个数组剩下最后一个数 且不相等 返回其中较小的那个即可 return min(arr1[l1],arr2[l2]); } };
import java.util.Arrays; import java.util.stream.IntStream; public class Solution { public int findMedianinTwoSortedAray(int[] arr1, int[] arr2) { int[] arr3 = IntStream.concat(Arrays.stream(arr1), Arrays.stream(arr2)).toArray(); Arrays.sort(arr3); return arr3[(arr3.length -1) >> 1]; } }
public int findMedianinTwoSortedAray (int[] arr1, int[] arr2) { // write code here //上中位数的下标 int k = arr1.length - 1; int i = 0, j = 0, s = 0; //遍历两个数组,比较两个数组元素大小,直到遍历次数=上中位数的下标 while (i < arr1.length && j < arr2.length) { if (arr1[i] <= arr2[j]) { if (s == k) { return arr1[i]; } i++; } else { if (s == k) { return arr2[j]; } j++; } s++; } return 0; }