LeetCode题目:4.Median of Two Sorted Arrays

来源:互联网 发布:数据归一化 代码 编辑:程序博客网 时间:2024/06/09 15:09

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 < value2double 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);}