LEETCODE的第四题——寻找两个正序数组的中位数(二分法)

LEETCODE的第四题——寻找两个正序数组的中位数(二分法)

本文主要用于记录刷力扣的题解,想通过深度学习力扣的每一道题目的原理来提高自己。
下面内容为自己学习时的记录,如有错误,欢迎指正🍭

一、题目

1

注意上面题目中有指定算法的时间复杂度为O(log(m+n))

二、思路分析与方法解析

2.1思路分析

错误思路
自己先开始想到的是将两个已知的数组合并为一个数组,然后根据合并后的数组长度分奇数偶数两种情况直接输出中位数的值。❌
这就不得不注意题目的要求了,就是之前提到的时间复杂度的问题。
反思一下自己,我觉得还是自己算法练习太少了,没有把摆脱这种低级的思路/(ㄒoㄒ)/~


正确的的思路

寻找两个正序数组的中位数,要求时间复杂度为 O(log(m + n)),有多种解决方法。以下是两种常见的方法:

2.1.1 方法1:二分查找法

这个方法通过在较短的数组上进行二分查找来实现 O(log(min(m, n))) 的时间复杂度。具体步骤如下:

  1. 确保 nums1 是较短的数组。如果不是,则交换 nums1 和 nums2,确保 nums1 是较短的数组。

  2. 在 nums1 上进行二分查找,将其分成两部分,左侧部分记为 left1,右侧部分记为 right1。

  3. 根据左侧部分和右侧部分的长度,计算在 nums2 上对应的位置 left2 和 right2。

  4. 检查 left1、right1、left2、right2 的值,以确定中位数。

具体的实现需要处理边界情况,例如当一个数组为空或者左侧部分的最大值小于右侧部分的最小值时,需要进行适当的调整。这个方法能够保证时间复杂度为 O(log(min(m, n)))

2.1.2 方法2:划分数组法

这个方法通过将两个数组分成左右两部分,确保左侧部分的元素小于右侧部分的元素,然后根据左侧部分和右侧部分的性质计算中位数。具体步骤如下:

  1. 使用二分查找法找到一个位置 i,将两个数组分成左右两部分,分别是 nums1[0:i] 和 nums2[0:j],其中 j = (m + n + 1) / 2 - i。

  2. 确保左侧部分的最大值小于等于右侧部分的最小值

  3. 根据左侧部分的最大值 max_of_left 和右侧部分的最小值 min_of_right,计算中位数

  4. 如果总元素个数为奇数,中位数就是 max_of_left;如果总元素个数为偶数,中位数就是 (max_of_left + min_of_right) / 2.0。

这个方法同样能够保证时间复杂度为 O(log(min(m, n)))

这两种方法都可以达到题目要求的时间复杂度,选择其中一种来实现即可。方法2通常更容易理解和实现。

2.2 方法解析

划分数组法和二分法都是用于解决寻找两个有序数组的中位数问题,但它们的思路和实现方式有一些不同之处

划分数组法(Median of Two Sorted Arrays)

  1. 划分为左右两部分:这个方法的核心思想是将两个有序数组分别划分成左右两部分,确保左侧部分的元素小于右侧部分的元素。这个划分是基于一个索引 i,将数组1分为 nums1[0:i] 和数组2分为 nums2[0:j],其中 j = (m + n + 1) / 2 - i。

  2. 检查左右部分的性质:在划分后,需要确保左侧部分的最大值(max_of_left)小于等于右侧部分的最小值(min_of_right)。这是为了满足中位数的定义。

  3. 计算中位数:根据左右部分的性质,计算中位数。如果总元素个数为奇数,中位数就是 max_of_left;如果总元素个数为偶数,中位数就是 (max_of_left + min_of_right) / 2.0。

  4. 时间复杂度:划分数组法的时间复杂度是 O(log(min(m, n))),其中 m 和 n 分别是两个数组的长度。

二分法

  1. 二分查找:这个方法主要是在较短的数组上进行二分查找,将其分成左侧部分(left1 和 left2)和右侧部分(right1 和 right2)。这个方法不涉及将数组划分成左右两部分,而是通过二分查找来定位中位数的位置。

  2. 时间复杂度:二分法的时间复杂度也是 O(log(min(m, n))),因为它主要是在较短的数组上进行二分查找,从而减小了查找的时间复杂度。

总的来说,两种方法都能够达到题目要求的时间复杂度,并且都能有效地寻找两个有序数组的中位数。选择哪种方法取决于个人偏好和实际实现情况。划分数组法通常更容易理解和实现,但二分法也是一个有效的选择,特别是在处理边界情况时可能更简单。

三、代码实现

3.1 二分法实现

c语言
double findMedianSortedArrays(int* nums1, int nums1Size, int* nums2, int nums2Size) {
    if (nums1Size > nums2Size) {
        return findMedianSortedArrays(nums2, nums2Size, nums1, nums1Size);
    }

    int i, j, left = 0, right = nums1Size;
    int halfLen = (nums1Size + nums2Size + 1) / 2;
    double median = 0.0;

    while (left <= right) {
        i = (left + right) / 2;
        j = halfLen - i;

        if (i < nums1Size && nums2[j - 1] > nums1[i]) {
            left = i + 1;
        } else if (i > 0 && nums1[i - 1] > nums2[j]) {
            right = i - 1;
        } else {
            int maxLeft, minRight;

            if (i == 0) {
                maxLeft = nums2[j - 1];
            } else if (j == 0) {
                maxLeft = nums1[i - 1];
            } else {
                maxLeft = fmax(nums1[i - 1], nums2[j - 1]);
            }

            if ((nums1Size + nums2Size) % 2 == 1) {
                return maxLeft;
            }

            if (i == nums1Size) {
                minRight = nums2[j];
            } else if (j == nums2Size) {
                minRight = nums1[i];
            } else {
                minRight = fmin(nums1[i], nums2[j]);
            }

            return (maxLeft + minRight) / 2.0;
        }
    }

    return median;
}


3.2 划分数组法

C

version 1 内存占用比较大

double findMedianSortedArrays(int* nums1, int nums1Size, int* nums2, int nums2Size) {
    if (nums1Size > nums2Size) {
        return findMedianSortedArrays(nums2, nums2Size, nums1, nums1Size);
    }

    int left = 0, right = nums1Size;
    int halfLen = (nums1Size + nums2Size + 1) / 2;

    while (left < right) {
        int partition1 = left + (right - left) / 2;
        int partition2 = halfLen - partition1;

        if (nums1[partition1] < nums2[partition2 - 1]) {
            left = partition1 + 1;
        } else {
            right = partition1;
        }
    }

    int partition1 = left;
    int partition2 = halfLen - left;

    int maxLeft1 = (partition1 == 0) ? INT_MIN : nums1[partition1 - 1];
    int minRight1 = (partition1 == nums1Size) ? INT_MAX : nums1[partition1];
    int maxLeft2 = (partition2 == 0) ? INT_MIN : nums2[partition2 - 1];
    int minRight2 = (partition2 == nums2Size) ? INT_MAX : nums2[partition2];

    if ((nums1Size + nums2Size) % 2 == 0) {
        return (double)(fmax(maxLeft1, maxLeft2) + fmin(minRight1, minRight2)) / 2.0;
    } else {
        return (double)fmax(maxLeft1, maxLeft2);
    }
    
}

version 2 查看了内存占用少的大佬的代码(没整明白QAQ)但是还是贴出来



double findMedianSortedArrays(int* nums1, int nums1Size, int* nums2, int nums2Size){
    int margin_left = 0, margin_right = nums1Size;
    int tmp1_left, tmp2_left, tmp1_right, tmp2_right;
    if(nums1Size==0){
        if(nums2Size&1)
            return nums2[nums2Size/2];
        else
            return (nums2[nums2Size/2-1] + nums2[nums2Size/2])/2.0;
    }
    if(nums2Size==0){
        if(nums1Size&1)
            return nums1[nums1Size/2];
        else
            return (nums1[nums1Size/2-1] + nums1[nums1Size/2])/2.0;
    }
    while(margin_left <= margin_right){
        int x = margin_left + (margin_right - margin_left) / 2;
        int y = (nums1Size + nums2Size) / 2 - x;
        if(y > nums2Size){
            margin_left = x;
            continue;
        }
        if(y < 0){
            margin_right = x;
            continue;
        }
        if(x) tmp1_left = nums1[x-1]; else tmp1_left = NULL;
        if(x != nums1Size) tmp1_right = nums1[x]; else tmp1_right = NULL;
        if(y) tmp2_left = nums2[y-1]; else tmp2_left = NULL;
        if(y != nums2Size) tmp2_right = nums2[y]; else tmp2_right = NULL; 
        
        if(tmp1_left!=NULL && tmp2_right!=NULL)
            if(tmp1_left > tmp2_right){
                margin_right = x;
                continue;
            }
        if(tmp2_left!=NULL && tmp1_right!=NULL)
            if(tmp2_left > tmp1_right){
                margin_left = x+1;
                continue;
            }
        break;
    }
    
    if((nums1Size + nums2Size) & 1){
        return tmp1_right == NULL? tmp2_right : (tmp2_right == NULL? tmp1_right: (tmp1_right > tmp2_right ? tmp2_right: tmp1_right));
    }
    else{
        int left, right;
        left = tmp1_left == NULL? tmp2_left:(tmp2_left==NULL?tmp1_left:(tmp1_left>tmp2_left?tmp1_left:tmp2_left));
        right = tmp1_right == NULL? tmp2_right : (tmp2_right == NULL? tmp1_right: (tmp1_right > tmp2_right ? tmp2_right: tmp1_right));
        return (left + right) / 2.0;
    }
    
}

python
class Solution(object):
    def findMedianSortedArrays(self, nums1, nums2):
        if len(nums1) > len(nums2):
            nums1, nums2 = nums2, nums1

        nums1Size, nums2Size = len(nums1), len(nums2)
        left, right = 0, nums1Size
        halfLen = (nums1Size + nums2Size + 1) // 2

        while left < right:
            partition1 = left + (right - left) // 2
            partition2 = halfLen - partition1

            if nums1[partition1] < nums2[partition2 - 1]:
                left = partition1 + 1
            else:
                right = partition1

        partition1 = left
        partition2 = halfLen - left

        maxLeft1 = float("-inf") if partition1 == 0 else nums1[partition1 - 1]
        minRight1 = float("inf") if partition1 == nums1Size else nums1[partition1]
        maxLeft2 = float("-inf") if partition2 == 0 else nums2[partition2 - 1]
        minRight2 = float("inf") if partition2 == nums2Size else nums2[partition2]

        if (nums1Size + nums2Size) % 2 == 0:
            return (max(maxLeft1, maxLeft2) + min(minRight1, minRight2)) / 2.0
        else:
            return max(maxLeft1, maxLeft2)

一般方法(虽然在时间复杂度上不符合,但是运行速度非常快)

int min(int x, int y)
{
    if (x < y){
        return x;
    } else {
        return y;
    }
}

double findMedianSortedArrays(int* nums1, int nums1Size, int* nums2, int nums2Size)
{
    int i,j,k;
    i = 0;
    j = 0;
    k = 0;
    int * num = malloc(sizeof(int) * (nums1Size + nums2Size));
    while (i < nums1Size && j < nums2Size) {
        //取两个数组中较小的数放入新数组中
        if(nums1[i] < nums2[j]){
            num[k++] = nums1[i++];
        } else {
            num[k++] = nums2[j++];
        }
    }
    //两个数组那个还有剩的
    if(i >= nums1Size){
        for(; j < nums2Size; j++){
            num[k++] = nums2[j];
        }
    } else {
        for(; i < nums1Size; i++){
            num[k++] = nums1[i];
        }
    }
    if((nums1Size + nums2Size) % 2 == 0){
        return (num[(nums1Size + nums2Size) / 2] + num[(nums1Size + nums2Size) / 2 - 1]) / 2.0;
    } else {
        return num[(nums1Size + nums2Size) / 2];
    }
}

写在最后
本文只为个人学习记录★,°:.☆( ̄▽ ̄)/$:.°★

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

写的什么石山代码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值