leetcode_4

7 篇文章 0 订阅
4 篇文章 0 订阅

@YangYang48

寻找两个有序数组的中位数

问题描述

给定两个大小为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

思路1:归并加查找

这里以c/c++语言来具体化

  1. 首先看到题眼有序数组,还是两个有序数组去找中位数,笔者第一反应考虑到归并排序中的归并
  2. 归并之后在选取中间的数即为中位数(偶数为两数之和,奇数为中间数)

输入:nums1=[1,3,5]
nums2=[1,4,7,8]
输出:4

归并排序中归并部分(具体可以参考归并排序

具体思路开始(可以参考下面几幅图)

  1. 建立str3[]数组,长度为len1+len2
  2. 将nums1[]和nums2[]的内容从小到大排列到新数组str3
  3. 找出str3中的中位数
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

代码

/*归并加查找*/
class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
       int len1=nums1.size();
        int len2=nums2.size();
        int *str3=new int[len1+len2];
        int i=0,j=0,k=0,l=0;
        while(i <len1&&j<len2)/*开始归并*/
        {
        	if(nums1[i]< nums2[j])
        		str3[k++]=nums1[i++];
        	else 
			str3[k++]=nums2[j++];
        }
        if(i<len1)
        {
            for(l=0;l<len1-i;l++)
        	str3[k+l]=nums1[i+l];
        }
        if(j<len2)
        {
            for(l=0;l<len2-j;l++)
            str3[k+l]=nums2[j+l];
        }
        int n=(len1+len2)/2;/*开始查找*/
        if(((len1+len2)&1)==1)//如果n是奇数
        	return (double)strs3[n];
        else 
		    return (double)(str3[n-1]+str3[n])/2; 
    }
};

根据图片可以知道中位数:奇数为str3[len/2],偶数为str3[len/2-1]和str3[len/2]加起来的平均值

思路2:二分查找

中位数理解:

将一个集合划分为两个长度相等的子集,其中一个子集中的元素总是大于另一个子集中的元素。

思路2主要使用了二分查找方法(具体实现点击这里),时间复杂度为o(logn)(可结合下图)

  1. 引入虚拟加入#来扩充数组,解决两个数组加起来不是偶数的问题(虚拟加入只是假象,并没有将原数组进行改变),将原数组元素虚拟各为2n+1个和2m+1个,总和为2n+2m+2

输入:nums1=[1,3,5]
nums2=[1,4,7,8]
假想:nums1=[#,1,#,3,#,5,#]
nums2=[#,1,#,4,#,7,#,8,#]

  1. 引入分割点,c1和c2,查找中位数为c1遍历nums1的所有可能,也就是七种可能,如果c1为#即为两数之间,c1为数字,即左半边和右半边都为次数,分成两个部分,两个部分都有这个数
  2. 引入i和j来对数组二分查找,对数组上下限low,high进行操作
  3. 设置四个标记点LMax1,LMax2,RMin1,RMin2用于判断二分查找点左右
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    中位数即为左边区域最大的数和右边区域最小的数总和的平均值
    (max(LMax1,LMax2)+min(RMin1,RMin2))/2.0

代码

/*哈希表*/
class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
       int n=nums1.size();
		int m=nums2.size();
		if (n>m)  //保证数组1一定最短
		{return findMedianSortedArrays(nums2,nums1);
		int LMax1,LMax2,RMin1,RMin2,c1, c2,low=0,high=2*n;  
		//我们目前是虚拟加了'#'所以数组1是2*n长度,数组2是2*m长度
		while (low<=high)   //二分
		{
			c1=(low+high)/2;  //c1是二分的结果
			c2=m+n-c1;//中位数作用,分成两堆要保证每一对数量相等,且一堆所有的数都大于另一堆数
			//设置四个标记点,用于判断循环
			LMax1=(c1==0)?INT_MIN:nums1[(c1-1)/2];
			RMin1=(c1==2*n)?INT_MAX:nums1[c1/2];
			LMax2=(c2==0)?INT_MIN:nums2[(c2-1)/2];
			RMin2=(c2==2*m)?INT_MAX:nums2[c2/2];
			if(LMax1>RMin2)
				high=c1-1;
			else if (LMax2>RMin1)
				low=c1+1;
			else
				break;
		}  
		return (max(LMax1,LMax2)+min(RMin1,RMin2))/2.0;//最后的2.0是强制转化为double类型
    }
};

二分查找更为简单,而且能够满足时间复杂度要求
如果本文对宝宝打开思路有帮助,可以点个赞哦~

参考

[1]扁扁熊
[2]力扣官方

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值