Q4 Median of Two Sorted Arrays 两个有序数组的中位数

本文介绍了一种高效算法,用于找出两个已排序数组的中位数,整体运行复杂度为O(log(m+n))。该算法巧妙地利用了二分查找的思想,通过递归划分并比较数组元素来定位中位数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目:

There are two sorted arrays nums1 and nums2 of size m and n respectively.

Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)).

Example 1:

nums1 = [1, 3]

nums2 = [2]

 

The median is 2.0

Example 2:

nums1 = [1, 2]

nums2 = [3, 4]

 

The median is (2 + 3)/2 = 2.5

----------------------------------------------------------------------------------

思路:

首先来定义一些符号。我们将输入的两个数组表示为a, b,并假定它们的元素个数分别为m, n . 其中, 表示a数组的第i个元素, 表示b数组的第i个元素。

然后,我们要先做一些假设,做这些假设是为了方便思考。(对于不符合这些假设的情况,我们将在后面进行处理)。

假设1:中位数出自数组a,并假定此中位数为 . (事实上,中位数有可能出自数组a,也有可能出自数组b )。

假设2:m+n为奇数

假设3:在中位数的两端,都既有a数组的元素也有b数组的元素

 

此时情况如下图所示:

其中,

(式 1)

(式2)

 

由于 是中位数,所以 两边的元素个数应该是相等的,即:

化简,得:

(式3)

注意,y与x的大小是反相关的。

至此,问题便转化为:利用数组的有序性,找出满足 式1、式2、式3 的

 

对于某个x,通过式3,y的值可以唯一确定(通过式3确定出来的y的实际意义是:当正好插入到后面时,两边的元素个数正好相等)

1.如果这组x,y也满足式2,则此 便是所求的中位数;

2.如果 ,则说明如果按照大小排序,应当插入到的前方。这意味着,左边的元素个数就会少于右边的元素个数。即当前的 太小了,亦即x太小了,所以需要在 [ x+1, r ] 的范围内继续搜索;

3.如果,则说明如果按照大小排序,应当插入到的后方。这意味着,左边的元素个数就会多于右边的元素个数。即当前的 太大了,亦即x太大了,所以需要在 [ l, x-1 ] 的范围内继续搜索;

 

通过上述描述,写出二分查找就很容易了。

接下来,我们对不满足三个假设的情况进行处理。

 

1.首先看假设1,对于中位数出自b的情况,其实很容易处理:如果在a中没有找到中位数,只需要将两个数组反着传入函数调用一次,在b中再找一遍就好了;

 

2.对于假设2,也比较好处理。我们假设为处于前面的那个中位数。此时 左边的元素比右边少一个,列出等式,化简,易得:。找到符合条件的后, 即为所求;

 

3.对于假设3,处理起来就有些麻烦了。其麻烦之处在于,通过式3求出来的y值可能不在[0,n-1]的值域里。针对落在不同的区域内的y的情况,我们需要分类讨论。

1)当 时,左边b元素的个数为负,无实际意义,说明x太大了,因此需要在[ l, x-1 ] 的范围内继续搜索

2)当 时,左边b元素的个数为0,有实际意义。此时式2弱化为

I)如果 ,则 即为所求;

II)如果 ,则x太大了,应该在[ l, x-1 ]的范围内继续搜索

3)当时,

I)如果这组x,y也满足式2,则此 便是所求的中位数;

II)如果 ,则说明如果按照大小排序,应当插入到的前方。这意味着,左边的元素个数就会少于右边的元素个数。即当前的 太小了,亦即x太小了,所以需要在 [ x+1, r ] 的范围内继续搜索

III)如果,则说明如果按照大小排序,应当插入到的后方。这意味着,左边的元素个数就会多于右边的元素个数。即当前的 太大了,亦即x太大了,所以需要在 [ l, x-1 ] 的范围内继续搜索

4)当时,右边b元素的个数为负,无实际意义。说明x太小了,所以需要在 [ x+1, r ] 的范围内继续搜索

 

我们对上述条件进行合并和化简,得:

If () || ( && )时,在 [ l, x-1 ] 的范围内继续搜索

If ( &&)|| (),在 [ x+1, r ] 的范围内继续搜索

Else,

If为奇数,返回

If为偶数,返回 (这里由于有x+1和y+1这种写法,因此下标有可能越界,在代码中可以看到我对越界的情况进行了处理)

 

-------------------------------------------------------------------------------------------------------------------

代码:

 

class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        Double rst = findMedium(nums1, nums2);
        if(rst != null) return rst;
        return findMedium(nums2, nums1);
    }
    
    private Double findMedium(int[] a, int[] b) {
    	int m = a.length, n = b.length;
    	boolean isOdd = ((m + n) % 2 == 1);
        int l = 0, r = m - 1;
        while( l <= r ) {
            int x = (l+r)/2;
            int y;
            if(isOdd) y = (m + n - 3 - 2*x) / 2;
            else y = (m + n - 4 - 2*x) / 2;
            if((y>=0 && y <= n-1 && a[x] < b[y]) || y > n - 1) {  // right
                l = x + 1;
            }else if((y<=-2) || (y>=-1 && y<n-1 && a[x] > b[y+1])) {  //left
                r = x - 1;
            }else {  //return answer
                if(isOdd) return (double) a[x];
                else {
                    return (double)(a[x] + Math.min(
                            x+1<=m-1?a[x+1]:Integer.MAX_VALUE, 
                            y+1<=n-1?b[y+1]:Integer.MAX_VALUE)) / 2;
                }
            }
        }
    	return null;
    }
}

内容概要:本文深入探讨了Kotlin语言在函数式编程和跨平台开发方面的特性和优势,结合详细的代码案例,展示了Kotlin的核心技巧和应用场景。文章首先介绍了高阶函数和Lambda表达式的使用,解释了它们如何简化集合操作和回调函数处理。接着,详细讲解了Kotlin Multiplatform(KMP)的实现方式,包括共享模块的创建和平台特定模块的配置,展示了如何通过共享业务逻辑代码提高开发效率。最后,文章总结了Kotlin在Android开发、跨平台移动开发、后端开发和Web开发中的应用场景,并展望了其未来发展趋势,指出Kotlin将继续在函数式编程和跨平台开发领域不断完善和发展。; 适合人群:对函数式编程和跨平台开发感兴趣的开发者,尤其是有一定编程基础的Kotlin初学者和中级开发者。; 使用场景及目标:①理解Kotlin中高阶函数和Lambda表达式的使用方法及其在实际开发中的应用场景;②掌握Kotlin Multiplatform的实现方式,能够在多个平台上共享业务逻辑代码,提高开发效率;③了解Kotlin在不同开发领域的应用场景,为选择合适的技术栈提供参考。; 其他说明:本文不仅提供了理论知识,还结合了大量代码案例,帮助读者更好地理解和实践Kotlin的函数式编程特性和跨平台开发能力。建议读者在学习过程中动手实践代码案例,以加深理解和掌握。
内容概要:本文深入探讨了利用历史速度命令(HVC)增强仿射编队机动控制性能的方法。论文提出了HVC在仿射编队控制中的潜在价值,通过全面评估HVC对系统的影响,提出了易于测试的稳定性条件,并给出了延迟参数与跟踪误差关系的显式不等式。研究为两轮差动机器人(TWDRs)群提供了系统的协调编队机动控制方案,并通过9台TWDRs的仿真和实验验证了稳定性和综合性能改进。此外,文中还提供了详细的Python代码实现,涵盖仿射编队控制类、HVC增强、稳定性条件检查以及仿真实验。代码不仅实现了论文的核心思想,还扩展了邻居历史信息利用、动态拓扑优化和自适应控制等性能提升策略,更全面地反映了群体智能协作和性能优化思想。 适用人群:具备一定编程基础,对群体智能、机器人编队控制、时滞系统稳定性分析感兴趣的科研人员和工程师。 使用场景及目标:①理解HVC在仿射编队控制中的应用及其对系统性能的提升;②掌握仿射编队控制的具体实现方法,包括控制器设计、稳定性分析和仿真实验;③学习如何通过引入历史信息(如HVC)来优化群体智能系统的性能;④探索中性型时滞系统的稳定性条件及其在实际系统中的应用。 其他说明:此资源不仅提供了理论分析,还包括完整的Python代码实现,帮助读者从理论到实践全面掌握仿射编队控制技术。代码结构清晰,涵盖了从初始化配置、控制律设计到性能评估的各个环节,并提供了丰富的可视化工具,便于理解和分析系统性能。通过阅读和实践,读者可以深入了解HVC增强仿射编队控制的工作原理及其实际应用效果。
题目描述: 给定两个大小为 m 和 n 的有序数组 nums1 和 nums2,请你找出这两个有序数组中位数,并且要求算法的时间复杂度为 O(log(m + n))。 示例 1: nums1 = [1, 3] nums2 = [2] 则中位数是 2.0 示例 2: nums1 = [1, 2] nums2 = [3, 4] 则中位数是 (2 + 3)/2 = 2.5 解题思路: 1. 将两个数组合并成一个数组,再对合并后的数组进行排序,找到中位数 时间复杂度为O(m+n)log(m+n),不符合题目要求 2. 利用二分法查找 首先,我们可以理解中位数的定义:如果某个有序数组的长度是奇数,那么其中位数就是最中间那个元素,如果是偶数,那么就是最中间两个元素的平均值。 在本题中,我们需要找到两个有序数组 A 和 B 的中位数,我们可以考虑从中位数的定义入手。如果我们能够将两个数组分别分成两部分,并且使得左半部分和右半部分分别满足以下两个条件: 1.左半部分的所有元素都小于右半部分的所有元素。 2.左半部分和右半部分分别包含了 A 和 B 数组的一半元素。 那么中位数就可以通过以下公式得到: median = max(left_part) (len(A) + len(B) 为偶数) median = (max(left_part) + min(right_part))/2 (len(A) + len(B) 为奇数) 为了使得划分满足上述两个条件,我们可以对较短的那个数组进行二分查找,然后根据查找到的位置将两个数组进行划分,最后再根据上述公式计算中位数。 具体实现见代码。 Java代码: class Solution { public double findMedianSortedArrays(int[] nums1, int[] nums2) { int m = nums1.length; int n = nums2.length; //确保nums1比nums2短 if (m > n) { int[] temp = nums1; nums1 = nums2; nums2 = temp; int tmp = m; m = n; n = tmp; } int iMin = 0, iMax = m; while (iMin <= iMax) { int i = (iMin + iMax) / 2; int j = (m + n + 1) / 2 - i; if (i < iMax && nums2[j - 1] > nums1[i]) { iMin = i + 1; } else if (i > iMin && nums1[i - 1] > nums2[j]) { iMax = i - 1; } else { int maxLeft = 0; if (i == 0) { maxLeft = nums2[j - 1]; } else if (j == 0) { maxLeft = nums1[i - 1]; } else { maxLeft = Math.max(nums1[i - 1], nums2[j - 1]); } if ((m + n) % 2 == 1) { return maxLeft; } int minRight = 0; if (i == m) { minRight = nums2[j]; } else if (j == n) { minRight = nums1[i]; } else { minRight = Math.min(nums1[i], nums2[j]); } return (maxLeft + minRight) / 2.0; } } return 0.0; } }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值