LeeCode4.寻找两个正序数组的中位数。小白都能懂。
叠甲,作者是算法白痴,举出的困惑点在大神看来显得很蠢。
一、题目核心(人话版)
给定两个从小到大排好序的数组,找它们合并后的中位数,要求时间复杂度
O(log(m+n))
(必须用二分,暴力合并数组会超时)。
总数奇数:中位数 = 中间那个数
总数偶数:中位数 = 中间两个数的平均值
二、我所有的核心困惑
- 分割数组后,左边的最大值不一定是 nums1 的分割点 a [i],会不会出错?
- 为什么只判断 a[i] <= b[j+1] 这一个条件,就能保证左边所有数 ≤ 右边所有数?
解疑惑
对于第一种。不会出现。后续有
intmaxLeft=Math.max(a[i],b[j]);intminRight=Math.min(a[i+1],b[j+1]);保证划分后的每组的值取正确。
对于第二个困惑。
对于 nums1和 nums2 把 nums1 的前 i 个元素给划分好的第一组。由于nums 本身就是有序的,所以能保证i 的值小于元 nums1 分给第二组的值。这时候,只要再小于 nums2划分给第二组的那些元素的最小值,就能保证 左边所有的数小于等于右边所有的数了
三、终极核心思想(一句话吃透)
我们不需要合并两个数组!把两个数组整体切一刀,平分成左右两堆,只要满足:
左边所有数字 ≤ 右边所有数字左边最大的数 + 右边最小的数 → 直接算出中位数!
四、我的顿悟时刻(解决所有困惑)
顿悟 1:数组有序 = 万能 buff(最关键!)
两个数组天生从小到大排序,这是所有逻辑的根基:
nums1 分割后:前 i 个数的最大值 = a [i],后面所有数 ≥ a [i+1]
nums2 分割后:前 j 个数的最大值 = b [j],后面所有数 ≥ b [j+1]
✅ 结论:我们只需要管 4 个边界值,剩下的数,有序规则帮我们全搞定!
顿悟 2:为什么只判断 a[i] <= b[j+1] 就够了?
a[i]:nums1 左边最大的数
b[j+1]:nums2 右边最小的数
只要「左边最大的数 ≤ 右边最小的数」,左边全体数字必然 ≤ 右边全体数字!不用判断任何其他数,这一个条件直接锁死分割正确性!
顿悟 3:左边最大值不是 a [i]?完全没问题!
分割后左边最大值可能是 a [i],也可能是 b [j],我们根本不在乎!最后一行代码 Math.max(a[i], b[j]) 会自动帮我们取左边最大值,完美兜底!
顿悟 4:二分短数组 = 偷懒防错
二分只操作更短的数组,长数组的分割点 j自动计算,既简化逻辑,又避免数组越界!公式:j = (总长度 + 1) / 2 - i(强制左右两堆数量相等 / 左边多 1 个)
五、保姆级代码步骤(逐行翻译)
- 步骤 1:保证 nums1 是短数组
- 二分只二分短数组,降低复杂度,避免越界。
- 步骤 2:加哨兵(负无穷 / 正无穷)
- 给数组前后加边界值,彻底解决分割到头部 / 尾部的越界问题,不用写任何边界判断。
- 步骤 3:二分找最优分割点 i
核心判断:a[i] <= b[j+1],满足就记录分割点,继续向右找更优解。 - 步骤 4:计算中位数
左边最大值:max(a[i], b[j])
右边最小值:min(a[i+1], b[j+1])
奇数返回左边最大值,偶数返回平均值。
六、完整正确代码(标注唯一易错点)
运行classSolution{publicdoublefindMedianSortedArrays(int[]nums1,int[]nums2){// 步骤1:永远把短数组放前面,二分只二分短数组if(nums1.length>nums2.length){int[]tmp=nums1;nums1=nums2;nums2=tmp;}intm=nums1.length;intn=nums2.length;// 步骤2:加哨兵(负无穷/正无穷),防止分割越界int[]a=newint[m+2];int[]b=newint[n+2];a[0]=b[0]=Integer.MIN_VALUE;a[m+1]=b[n+1]=Integer.MAX_VALUE;System.arraycopy(nums1,0,a,1,m);System.arraycopy(nums2,0,b,1,n);// 步骤3:二分查找最优分割点iintleft=0,right=m;intbestI=0;while(left<=right){inti=(left+right)>>>1;// 防溢出写法intj=(m+n+1)/2-i;// 自动计算j// 核心判断:锁死左≤右if(a[i]<=b[j+1]){bestI=i;left=i+1;}else{right=i-1;}}// 步骤4:计算中位数inti=bestI;intj=(m+n+1)/2-i;// ✅ 唯一易错点:b[j] 千万别写成 b[i]!intmaxLeft=Math.max(a[i],b[j]);intminRight=Math.min(a[i+1],b[j+1]);// 奇数/偶数判断return(m+n)%2>0?maxLeft:(maxLeft+minRight)/2.0;}}七、终极总结(新手必背)
有序数组是爹:所有判断都依赖数组天生有序,只看 4 个边界值就够了;
核心判断:a[i] <= b[j+1] → 左边全体 ≤ 右边全体;
不用纠结左边最大值是谁:max(a[i],b[j]) 自动兜底;
二分短数组:简单、高效、不越界;
哨兵机制:解决所有边界问题,代码更简洁。
这道 Hard 题,吃透「分割思想 + 数组有序」这两个点,直接降维打击!希望我的总结能帮到和我一样的算法小白~
