LeetCode题目:4.Median of Two Sorted Arrays

LeetCode题目:4.Median of Two Sorted Arrays

原题链接:https://leetcode.com/problems/median-of-two-sorted-arrays/description/

解题思路:

由于时间复杂度限制在了 O(log (m+n)),因此不能遍历整个数组。由于是log,因此比较容易想到的是关于二分的算法。

在一个排序好的数组中,查找一个特定值所需的时间为O(log n),类似的,给定一个值x,用二分方法将这个值插入一个排好序的数组a,所需要的时间也为O(log n)。插入的同时,也能知晓给定值x在数组a中排第几位。所以,求中位数可等价于求整个数组中的第n/2大的数。

如题,给定两个排好序的数组a和b,他们的数量分别是n和m,取数组a中的中位数,在数组b中进行二分插入,即可该中位数在整个数组和中的位置,然后和要求的位置比较,如果相等,得到答案。如果不等,舍弃a和b数组的一部分,取另一部分继续上述操作即可。

核心思想:

步骤如下:

设初始目标位置(中位值在总数组的位置)为(m+n)/2。

1.取a或b数组中的某数x(为了尽可能每次筛选足够多的数,x应在大数组中取数,并且使比值相同,即 x位置/大数组数量 = 目标位置/数组总数)。

2.在另一个数组中,二分插入x,得到x在该数组中的位置。

3.求得x在总数组中的位置,与目标位置进行比较。

4.如果相同,该值即为中位值。如果不相同,取两数组各取其中一部分,并修改目标位置,返回1。

代码细节:

1.可能出现二分的时候插入值比整个数组都大或者比整个数组都小

2.直接截取数组可能会花费过多时间,因此修改数组begin和end值来截取数组。

3.若为偶数,有两个中位值,可以计算前,拿出数组中的最小值,这样两个中位值变成了一个中位值和中位值前继。


坑点:

1.其中之一数组为空。

2.二分插入时插入值可能比整个数组都大,要特殊讨论。

3.当总数组只有两个数时,去掉最小值会导致答案错误。


代码:

// 二分查找,查找数字target在nums.begin---nums.end之中的位置
// target刚好小于等于nums[return int] 
int findTargetPositionInNums(vector<int>& nums, int begin, int end, int target) {
    // 如果区间为空,直接返回
    if (begin == end)  return begin; 

    int mid = (end + begin) / 2;

    while (1) {
        // 区间长度小于1,进行比较 
        if (end <= begin + 1)
            if (target > nums[mid])  return end;
            else  return begin;

        // nums[mid - 1] <= nums[mid] < target 
        if (target > nums[mid])  begin = mid + 1;
        // nums[mid - 1] < target <= nums[mid],返回结果 
        else if (target > nums[mid - 1])  return mid;
        // target <= nums[mid - 1] <= nums[mid]
        else  end = mid;

        mid = (end + begin) / 2;
    }
}

// 根据总数返回中位值,其中value1 < value2
double getMedian(int value1, int value2, bool isEven) {
    return isEven ? (double)(value1 + value2) / 2 : value2;
}

double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
    int sumSize = nums1.size() + nums2.size();     // 总数 
    bool isEven = sumSize % 2 == 0;                // 判断中位数数量 

    // 当数量太小 
    if (sumSize <= 2) {
        nums1.insert(nums1.end(), nums2.begin(), nums2.end());
        if (isEven)  return (double)(nums1[0] + nums1[1]) / 2;
        return nums1[0];
    }

    // 当其中一个数组为空 
    if (nums1.empty())
        return getMedian(nums2[nums2.size() / 2 - 1], nums2[nums2.size() / 2], isEven);
    else if (nums2.empty())
        return getMedian(nums1[nums1.size() / 2 - 1], nums1[nums1.size() / 2], isEven);

    // 正常数组
    // 如果是偶数,去掉最小值,使中位数只有一个 
    if (isEven) {
        if (nums1[0] < nums2[0])  nums1.erase(nums1.begin());
        else  nums2.erase(nums2.begin());
        sumSize--;
    }

    int begin1 = 0; 
    int end1 = nums1.size();
    int begin2 = 0;
    int end2 = nums2.size();               // 用于递归的值 

    int medianPosInSum = sumSize / 2 + 1;  // 中位数在总数组中第几大,一开始为sumSize/2+1
    int posInBig;                          // 某数在大数组中的位置 
    int posInSmall;                        // 某数在小数组中的位置 
    int posInSum;                          // 某数在总数组中的位置 
    int isInNums1;                         // 判断最后得到的中位值在nums1还是nums2 

    while (1) {
        if (end1 - begin1 > end2 - begin2) {
            // 从大数组中取一个可能的位置,为了降低时间复杂度,取比例位置 
            posInBig = (float)medianPosInSum / sumSize * (end1 - begin1) + begin1;
            // 在小数组中找到相应的位置 
            posInSmall = findTargetPositionInNums(nums2, begin2, end2, nums1[posInBig]);
            // 求出该数在总数组的位置
            posInSum = (posInBig - begin1 + 1) + (posInSmall - begin2);

            // 将posInSum和medianPosInSum比较 
            // posInSum < medianPosInSum,砍掉左半部分 
            if (posInSum < medianPosInSum) {
                medianPosInSum -= posInSum;
                begin2 = posInSmall;
                begin1 = posInBig + 1;
            }
            // posInSum > medianPosInSum,砍掉右半部分 
            else if (posInSum > medianPosInSum) {
                end2 = posInSmall;
                end1 = posInBig;
            }
            // posInSum == medianPosInSum,得到结果,跳出循环
            else {
                isInNums1 = true; 
                break;
            } 
        }
        // 对称 
        else {
            posInBig = (float)medianPosInSum / sumSize * (end2 - begin2) + begin2;
            posInSmall = findTargetPositionInNums(nums1, begin1, end1, nums2[posInBig]);
            posInSum = (posInBig - begin2 + 1) + (posInSmall - begin1);
            if (posInSum < medianPosInSum) {
                medianPosInSum -= posInSum;
                begin1 = posInSmall;
                begin2 = posInBig + 1;
            }
            else if (posInSum > medianPosInSum) {
                end1 = posInSmall;
                end2 = posInBig;
            }
            else {
                isInNums1 = false; 
                break;
            } 
        }
    }

    // 求出中位值和中位值的前一个值
    int median = isInNums1 ? nums1[posInBig] : nums2[posInBig];
    int preMedian = isInNums1 ? max(nums1[posInBig - 1], nums2[posInSmall - 1])
               : max(nums2[posInBig - 1], nums1[posInSmall - 1]);

    return getMedian(preMedian, median, isEven);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值