4. 寻找两个正序数组的中位数

题目描述

给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。

算法的时间复杂度应该为 O(log (m+n)) 。

题目连接

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

测试用例

输入:nums1 = [1,3], nums2 = [2]
输出:2.00000
解释:合并数组 = [1,2,3] ,中位数 2

输入:nums1 = [1,2], nums2 = [3,4]
输出:2.50000
解释:合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5

解法1:合并数组

思路

直接将两个数组进行合并,根据合并后数组元素个数的奇偶性求得中位数

解题代码

class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
    	//合并后的数组
        int[] sum = new int[nums1.length+nums2.length];
        //i,j分别为nums1和nums2的数组下标,n1,n2对应数组下标的值
        //k代表合并后的数组的数组下标
        int i=0, j=0, k=0, n1, n2;
        while(i<nums1.length || j<nums2.length){
        	//注意这里是将数据较小的放到sum数组中,所以如果某数组元素为空,应当让其成为最大值
            n1 = i<nums1.length ? nums1[i] : (int)Math.pow(10,6);
            n2 = j<nums2.length ? nums2[j] : (int)Math.pow(10,6);
            if(n1 < n2){
                sum[k] = n1;
                i++;
                k++;
            }else{
                sum[k] = n2;
                j++;
                k++;
            }
        }
        //double类型!避免相除有余数
        double result;
        if(sum.length%2 == 0){
            //注意要强制转换为doble类型
            result = ((double)sum[sum.length/2]+sum[sum.length/2-1])/2;
        }else{
            result = sum[sum.length/2];
        }
        return result;
    }
}

存在问题

简单的合并数组,时间复杂度是O(m+n),不满足题目中的O(log(m+n))的要求

解法2

在每次判断的时候去除不可能是中位数的元素

思路

  1. 通过递归方式求解
  2. 找数组nums1与nums2中第k个大的元素,可以找两个数组的第k/2个元素,然后判断两元素的大小
  3. 如果nums1的第k/2个元素小的话,说明数组nums1中前k/2个元素一定不符合要求!则问题转化为了求(nums1-前k/2个元素)与nums2 的第(k-nums1删除的元素个数)大的元素
  4. 注意这里不一定对比的是 nums1和nums2中的第k/2个元素,需要考虑这两个数组中是否含有k/2个元素,如果没有,则判断该数组的最后一个元素
  5. 递归退出的条件
    (1)有个数组长度为0,返回另一个数组的第k个元素
    (2)k=1,返回目前两个数组中第一个元素的最小值
    注意:这两个条件不能调换顺序!避免数组越界
  6. 小技巧:两个数组元素个数相加为奇数或者偶数,影响对中位数的求解,将其转化为一种情况便于求解:都求平均值

解题代码

class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        //记录两个数组长度
        int nums1Length = nums1.length;
        int nums2Length = nums2.length;
        //将奇数个或者偶数个数组个数合并,最终求的都是第left和right个数的平均值
        //合并后的数组元素个数有奇数个:12345,left=right=3【待合并数组的元素个数分别为2 3】
        //合并后的数组元素个数有偶数个:123456,left=3,right=4【待合并数组的元素个数分别为3 3】
        int left = (nums1Length + nums2Length + 1) / 2;
        int right = (nums1Length + nums2Length + 2) / 2;//默认向下取整
        //最终结果为第right和第left个数组元素平均值
        return (findKth(nums1, 0, nums1Length-1, nums2, 0, nums2Length-1, left) + 
                findKth(nums1, 0, nums1Length-1, nums2, 0, nums2Length-1, right))/2;
    }
    /**
    找两个数组中第k个大的元素。
    如果num1中的第k/2个大的元素<num2中第k/2个大的元素,说明num1中前k/2个元素不符合要求
    题目就变成了求(去除num1中的前k/2个元素的num1)与len2 的第(k/2-去除元素个数)个元素
    退出递归条件:(1)k=1,比较两个数组的开始值的大小,返回最小的那个(2)某个数组中元素为空,直接返回另一个数组中的第k个元素

    待传入参数:两个数组,两个数组的start和end(比较的是两数组中的数组下标为start到end中间的数),k(需要查找第k个元素)
     */
    public double findKth(int[] nums1, int start1, int end1, int[] nums2, int start2, int end2, int k){
        int length1 = end1 - start1 + 1;//num1的数组长度
        int length2 = end2 - start2 + 1;//num2的数组长度

        //如果两个数组长度不同,nums1始终为长度较小的数组(便于退出递归条件的书写)
        if(length1 > length2){
            return findKth(nums2, start2, end2, nums1, start1, end1, k);
        }

        //退出递归的条件,注意1与2的顺序!避免越界
        //1. 有一个数组为空,即找另一个数组的第k个元素
        if(length1 == 0){
            //返回变化后的num2中第k个元素(目前nums2下标从start2开始)
            return nums2[start2 + k - 1];
        }
        //2. k=1,即找两个数组中第1个小的数值
        if(k == 1){
            //返回两个数组起始下标对应元素较小的哪一个
            return Math.min(nums1[start1], nums2[start2]);
        }
        

        //循环递归,如果一个数组num1的第k/2个元素比另一个数组num2小,则num1中的前k/2个元素均不是最小值
        //可以将num1中前k/2个元素去除(修改num1的start),然后查找 修改后的num1 和 初始num2的 第(k-删除num1的元素个数)个元素
        //注意,判断数组长度与k/2的大小,应当取较小值,避免越界

        int i = Math.min(start1 + length1 -1, start1 + k/2 -1);//记录num1待判断元素的数组下标
        int j = Math.min(start2 + length2 -1, start2 + k/2 -1);//记录num2待判断元素的数组下标
        //-1是因为数组下标从0开始

        //num2的前j个元素被删去
        if(nums1[i] > nums2[j]){
            //一共删除了j-start2+1个元素
            return findKth(nums1, start1, end1, nums2, j+1, end2, k-(j - start2 + 1));
        }else{
            return findKth(nums1, i+1, end1, nums2, start2, end2, k-(i - start1 + 1));
        }

    }
}

Java边学边用

指数

求指数Math.pow

double pow(double base, double exponent)
//返回的结果是double形式,注意格式转换
(int)Math.pow(10,6)

除法运算

除法运算时,两个整数相除,想得到double类型的数据,需要先将其中的一个除数转化为double类型,否则得到的一直是整数类型的数据。
eg:(3+2)/2=2,((double)3+2)/2=2.5

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
寻找两个正序数组中位数可以使用归并的方式,合并两个有序数组,得到一个大的有序数组。然后找到大的有序数组的中间位置的元素,即为中位数。另一种方法是使用双指针的方式,维护两个指针,初始时分别指向两个数组的下标0的位置。每次将指向较小值的指针后移一位(如果一个指针已经到达数组末尾,则只需要移动另一个数组的指针),直到到达中位数的位置。这样可以在O(log(m+n))的时间复杂度内找到中位数。 具体步骤如下: 1. 初始化指针p1和p2分别指向两个数组的起始位置0。 2. 判断两个指针所指的元素大小,较小的元素所在的指针后移一位,直到其中一个指针到达数组末尾。 3. 若两个数组的长度之和为奇数,那么中位数即为当前指针指向的元素; 若两个数组的长度之和为偶数,那么中位数为当前指针指向的元素与其下一个元素的平均值。 4. 返回中位数作为结果。 需要注意的是,为了保证时间复杂度为O(log(m+n)),在每次移动指针时,应该移动的步数应该是当前指针所在数组长度的一半,即k/2,其中k为两个数组的长度之和。 以下是一个示例代码,用于说明上述方法的实现: ``` int findMedianSortedArrays(int[] nums1, int[] nums2) { int m = nums1.length; int n = nums2.length; int total = m + n; int middle = total / 2; int p1 = 0, p2 = 0; int prev = 0, curr = 0; for (int i = 0; i <= middle; i++) { prev = curr; if (p1 < m && (p2 >= n || nums1[p1 < nums2[p2])) { curr = nums1[p1++]; } else { curr = nums2[p2++]; } } if (total % 2 == 0) { return (prev + curr) / 2; } else { return curr; } } ``` 该方法可以在O(log(m+n))的时间复杂度内找到两个正序数组中位数。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [算法寻找两个正序数组中位数。](https://blog.csdn.net/en_joker/article/details/107179641)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [寻找两个正序数组中位数](https://blog.csdn.net/wulila/article/details/124483500)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值