LeetCode之旅(C/C++):4. 两个排序数组的中位数

版权声明:欢迎转载,不必客气。 https://blog.csdn.net/CSDN_FengXingwei/article/details/82227676

1、题目描述

给定两个大小为 m 和 n 的有序数组 nums1 和 nums2 。
请找出这两个有序数组的中位数。要求算法的时间复杂度为 O(log (m+n)) 。
你可以假设 nums1 和 nums2 不同时为空。

示例 1:

nums1 = [1, 3]
nums2 = [2]
中位数是 2.0

示例 2:

nums1 = [1, 2]
nums2 = [3, 4]
中位数是 (2 + 3)/2 = 2.5

2、代码实现

2.1 C++语言

参考文档:【分步详解】两个有序数组中的中位数和Top K问题

//O(log(m+n))
class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) 
    {
        int nums1Size = nums1.size();
        int nums2Size = nums2.size();
        if (nums1Size > nums2Size)   //保证数组1一定最短
            return findMedianSortedArrays(nums2, nums1);
        int L1, L2, R1, R2, C1, C2, low = 0, high = 2 * nums1Size;  //虚拟加了'#'所以数组1是2*n+1长度
        while (low <= high)   //二分
        {
            C1 = (low + high) / 2;  //C1是二分的结果
            C2 = nums1Size + nums2Size - C1; //关键点
            L1 = (C1 == 0) ? INT_MIN : nums1[(C1 - 1) / 2];   //nums1整体比中位数大,只能选L2
            R1 = (C1 == 2 * nums1Size) ? INT_MAX : nums1[C1 / 2];   //nums1整体比中位数小,只能选R2
            L2 = (C2 == 0) ? INT_MIN : nums2[(C2 - 1) / 2]; //nums2整体比中位数大,只能选L1
            R2 = (C2 == 2 * nums2Size) ? INT_MAX : nums2[C2 / 2];   //nums2整体比中位数小,只能选R1
            if (L1 > R2)//C1割太大,左移
                high = C1 - 1;
            else if (L2 > R1)//C1割太小,右移
                low = C1 + 1;
            else//找到合适的割,即在两个数组中,整个左部小于整个右部
                break;
        }
        return (max(L1, L2) + min(R1, R2)) / 2.0;
    }
};

2.2 C语言(3种)

第1种

//O(m+n)
//合并两有序数组成一个新有序数组,再按中间位置取值
double findMedianSortedArrays(int* nums1, int nums1Size, int* nums2, int nums2Size) 
{
    int nums12Size = nums1Size + nums2Size;
    //int *nums12[nums12Size]; //C语言不支持
    int *nums12 = (int*)malloc(sizeof(int) * nums12Size);
    int i = 0, j = 0, k = 0;
    while(i < nums1Size && j < nums2Size)
    {
        if(nums1[i] < nums2[j])
        {
            nums12[k] = nums1[i];
            ++i;
            ++k;
        }
        else
        {
            nums12[k] = nums2[j];
            ++j;
            ++k;
        }
    }
    while(i < nums1Size)
    {
        nums12[k] = nums1[i];
        ++i;
        ++k;
    }
    while(j < nums2Size)
    {
        nums12[k] = nums2[j];
        ++j;
        ++k;
    }
    double middle;
    if(nums12Size % 2 == 0)
        middle = (nums12[nums12Size / 2 - 1] + nums12[nums12Size / 2]) * 1.0 / 2;
    else
        middle = nums12[nums12Size / 2];
    return middle;
}

第2种

//O(k)
//两个指针分别从两数组开头指,比较两指针处的数,谁小谁往后移,
//直到两指针共移动k次,k为中间位置
//可以理解成当前下标前面的元素都是归并有序的,而不包括当前下标所指向的元素
double findMedianSortedArrays(int* nums1, int nums1Size, int* nums2, int nums2Size) 
{
    int nums12Size = nums1Size + nums2Size;
    int index1 = 0; //移动次数和下标,刚好是一一对应关系
    int index2 = 0; 
    double middle = 0;
    while (index1 + index2 < (nums12Size + 2) / 2 ) //移动次数之和小于K
    {
        if (index1 < nums1Size) //移动数组1
        {
            while (index2 == nums2Size || nums1[index1] <= nums2[index2]) 
            {
                index1++;
                //无论奇偶都用中间的两个数计算中位数,只是奇数时中间的两个数在同一个位置
                if (index1 + index2 == (nums12Size + 1) / 2)
                {
                    middle += nums1[index1 - 1];
                }
                if (index1 + index2 == (nums12Size + 2) / 2)
                {
                    middle += nums1[index1 - 1];
                }
                if (index1 == nums1Size) //数组1整体比中位数小
                {
                    break;
                }
            }
        }
        if (index2 < nums2Size) //移动数组2
        {
            while (index1 == nums1Size || nums2[index2] <= nums1[index1]) 
            {
                index2++;
                if (index1 + index2 == (nums12Size + 1) / 2)
                {
                    middle += nums2[index2 - 1];
                }
                if (index1 + index2 == (nums12Size + 2) / 2)
                {
                    middle += nums2[index2 - 1];
                }
                if (index2 == nums2Size) //数组2整体比中位数小
                {
                    break;
                }
            }
        }
    }
    return middle / 2;
}

第3种

//O(log(m+n))
//两个数组分别采用二分法查找
//寻找第K小的数
//与C++代码的二分有所区别
double findMedianSortedArrays(int* nums1, int nums1Size, int* nums2, int nums2Size)
{
    int nums12Size = nums1Size + nums2Size;
    int left = (nums12Size + 1) / 2;    //左中位数的位置,位置是从1开始的,下标是从0开始的
    int right = (nums12Size + 2) / 2;   //右中位数的位置
    if (left == right)  //奇数
    {
        return getKMin(nums1, nums1Size, 0, nums2, nums2Size, 0, left);
    }
    else//偶数
    {
        return (getKMin(nums1, nums1Size, 0, nums2, nums2Size, 0, left) + \
            getKMin(nums1, nums1Size, 0, nums2, nums2Size, 0, right)) * 1.0 / 2;
    }
}

//注意:k和数组都是递归变化的
int getKMin(int* nums1, int nums1Size, int nums1Start, int* nums2, int nums2Size, int nums2Start, int k)
{
    if (nums1Start > nums1Size - 1) //数组1整体比中位数小,中位数在数组2中
    {
        return nums2[nums2Start + k - 1];
    }
    if (nums2Start > nums2Size - 1) //数组2整体比中位数小,中位数在数组1中
    {
        return nums1[nums1Start + k - 1];
    }
    if (k == 1) //要找的是第1小的数,分两种情况,一种是两个数组共有2个以内的元素,另一种是递归到k=1
    {
        return nums1[nums1Start] < nums2[nums2Start] ? nums1[nums1Start] : nums2[nums2Start];
    }
    int nums1Min = INT_MAX, nums2Min = INT_MAX;
    if (nums1Start + k / 2 - 1 < nums1Size) //当一个数组很短,比中位数的位置还短时,就先不管它
    {
        nums1Min = nums1[nums1Start + k / 2 - 1];
    }
    if (nums2Start + k / 2 - 1 < nums2Size) 
    {
        nums2Min = nums2[nums2Start + k / 2 - 1];
    }
    //每次都从“较小”的数组中“删掉”前k/2个元素,直到将其中的一个数组全部删掉,或者k=1时,就能在下一次递归中找到目标元素
    return nums1Min < nums2Min ? \
        getKMin(nums1, nums1Size, nums1Start + k / 2, nums2, nums2Size, nums2Start, k - k / 2) : \
        getKMin(nums1, nums1Size, nums1Start, nums2, nums2Size, nums2Start + k / 2, k - k / 2);
}

猜你喜欢

转载自blog.csdn.net/CSDN_FengXingwei/article/details/82227676
今日推荐