给你两个有序的数组,求中位数
双指针查找,每次把k减少一定的值,直到k=1,取两个指针的最小值。
public class _decrease__k {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int len1 = nums1.length;
int len2 = nums2.length;
int left = (len1 + len2 + 1) / 2;
int right = (len1 + len2 + 2) / 2;
//将偶数和奇数的情况合并,如果是奇数,会求两次同样的 k 。
return (getKth(nums1, 0, len1 - 1, nums2, 0, len2 - 1, left) +
getKth(nums1, 0, len1 - 1, nums2, 0, len2 - 1, right)) * 0.5;
//中位数就是合并之后第(len1+len2)小的数,所以最后一个参数第k大的数就是中位数的位置。
// 这里是为了处理奇偶,算了两次,对于奇数,left == right,求出来的是一个值,对于偶数,求的是
//(len1+len2)/2和(len1+len2)/2 +1 项的平均数
}
//给定两个数组的范围,求出其中第k大的元素,k是最后一个参数
private int getKth(int[] nums1, int start1, int end1, int[] nums2, int start2, int end2, int k) {
int len1 = end1 - start1 + 1;
int len2 = end2 - start2 + 1;
//给定范围内的数组长度,也可以理解是有效长度
//让 len1 的长度小于 len2,这样就能保证如果有数组空了,一定是 len1为空
if (len1 > len2) return getKth(nums2, start2, end2, nums1, start1, end1, k);
if (len1 == 0) return nums2[start2 + k - 1];//递归的出口1,一个数组长度小于k/2,而且都不符合
if (k == 1) return Math.min(nums1[start1], nums2[start2]);//递归的出口2,两个都符合,而且到了只选一个的时候
int i = start1 + Math.min(len1, k / 2) - 1;
int j = start2 + Math.min(len2, k / 2) - 1;
//如果k太大,导致越界,就少走一点,保证不会越界
//小的那个前面的都,不要了,最后一个参数是对k的调整比如丢掉了3个数,那就求第四小的数
if (nums1[i] > nums2[j]) {
return getKth(nums1, start1, end1, nums2, j + 1, end2, k - (j - start2 + 1));
}
else {
return getKth(nums1, i + 1, end1, nums2, start2, end2, k - (i - start1 + 1));
}
}
}
定义法,把中位数视作分隔符,确保两边的性质符合要求的时候,就是找到中位数的时候。
关于中位数的定义:可将数值集合划分为相等的上下两部分
对于A.B,2个数组,假设A有m个元素,B有n个元素
分别在i和j的位置进行划分,把A的0---i-1号元素和B的0---j-1号元素放到左边,剩下的放到右边
保证:如果m+n是奇数,左边元素比右边元素多1,如果m+n是偶数,左右元素应该相等
同时左边最大值应该小于右边最小值
利用有序的前提
偶数:i+j = m-i+n-j,奇数:i+j = m-i+n-j+1
偶数:i+j = (m+n)/2,奇数:i+j = (m+n+1)/2
对于偶数,1/2=0,所以可以奇偶合并,j = (m+n+1)/2 - i
为了保证j不越界,限制 m<n,证明如下:
m≤n, i<m , j = (m+n+1)/2−i ≥ (m+m+1)/2−i > (m+m+1)/2−m = 0
m≤n, i>0 , j = (m+n+1)/2−i ≤ (n+n+1)/2−i < (n+n+1)/2 = n
因为有序,所以
max(A[i-1],B[j-1]) < min(A[i],B[j]),再用一次有序,A[i-1]<A[i],B[j-1]<B[j]
只要 A[i-1]<=B[j],B[j-1] <= A[i]
如果A[i-1]>B[j],说明A的元素大,需要减小A的i
B[j-1] > A[i],说明B的元素大,需要增加A的i
因为i和j的关系,减小i就是在增加j
当i等于0,左半边最大值B[j-1],因为A的左半边没了
j=0,同理
i等于m,右半边最小值B[j]
j=n,同理
增加 i 的方式。当然用二分了。初始化 i 为中间的值,然后减半找中间的,减半找中间的,减半找中间的直到答案
public class Conception_of_middle_number {
//关于中位数的定义:可将数值集合划分为相等的上下两部分
//对于A.B,2个数组,假设A有m个元素,B有n个元素
//分别在i和j的位置进行划分,把A的0---i-1号元素和B的0---j-1号元素放到左边,剩下的放到右边
//保证:如果m+n是奇数,左边元素比右边元素多1,如果m+n是偶数,左右元素应该相等
//同时左边最大值应该小于右边最小值
//利用有序的前提
//偶数:i+j = m-i+n-j,奇数:i+j = m-i+n-j+1
//偶数:i+j = (m+n)/2,奇数:i+j = (m+n+1)/2
//对于偶数,1/2=0,所以可以奇偶合并,j = (m+n+1)/2 - i
//为了保证j不越界,限制 m<n,证明如下:
// m≤n, i<m , j = (m+n+1)/2−i ≥ (m+m+1)/2−i > (m+m+1)/2−m = 0
// m≤n, i>0 , j = (m+n+1)/2−i ≤ (n+n+1)/2−i < (n+n+1)/2 = n
//因为有序,所以
// max(A[i-1],B[j-1]) < min(A[i],B[j]),再用一次有序,A[i-1]<A[i],B[j-1]<B[j]
//只要 A[i-1]<=B[j],B[j-1] <= A[i]
//如果A[i-1]>B[j],说明A的元素大,需要减小A的i
//B[j-1] > A[i],说明B的元素大,需要增加A的i
// 因为i和j的关系,减小i就是在增加j
//当i等于0,左半边最大值B[j-1],因为A的左半边没了
//j=0,同理
//i等于m,右半边最小值B[j]
//j=n,同理
//增加 i 的方式。当然用二分了。初始化 i 为中间的值,然后减半找中间的,减半找中间的,减半找中间的直到答案
public double findMedianSortedArrays(int[] A, int[] B) {
int m = A.length;
int n = B.length;
if (m > n) {
return findMedianSortedArrays(B,A); // 保证 m <= n
}
int iMin = 0, iMax = m;
while (iMin <= iMax) {
int i = (iMin + iMax) / 2;//第一刀切在中间,
int j = (m + n + 1) / 2 - i;//这是公式
if (j != 0 && i != m && B[j-1] > A[i]){ // i 需要增大
iMin = i + 1;
}
else if (i != 0 && j != n && A[i-1] > B[j]) { // i 需要减小
iMax = i - 1;
}
else { // 达到要求/边界条件列出来单独考虑
int maxLeft = 0;
if (i == 0) { maxLeft = B[j-1]; }
else if (j == 0) { maxLeft = A[i-1]; }
else { maxLeft = Math.max(A[i-1], B[j-1]); }
if ( (m + n) % 2 == 1 ) { return maxLeft; } // 奇数的话不需要考虑右半部分
int minRight = 0;
if (i == m) { minRight = B[j]; }
else if (j == n) { minRight = A[i]; }
else { minRight = Math.min(B[j], A[i]); }
return (maxLeft + minRight) / 2.0; //如果是偶数的话返回结果
}
}
return 0.0;
}
}