寻找中位数

给你两个有序的数组,求中位数

 双指针查找,每次把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;
        }
    }

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值