前言
这几天不是在写文章总结黑马点评吗,感觉敲代码的能力确实有点欠缺了,主要是敲项目代码的时候,接入了AI,大部分代码都是直接按Tab键自动生成的,代码能力确实有一点退步了🤪🤪🤪,这几天学学数据结构,刷刷题🏄🏄🏄,冲冲冲一天一道,(但是我感觉可能坚持不下去🤪)
题目
给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。
算法的时间复杂度应该为 O(log (m+n)) 。
示例 1:
输入:nums1 = [1,3], nums2 = [2]
输出:2.00000
解释:合并数组 = [1,2,3] ,中位数 2
示例 2:
输入:nums1 = [1,2], nums2 = [3,4]
输出:2.50000
解释:合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5
提示:
nums1.length == m
nums2.length == n
0 <= m <= 1000
0 <= n <= 1000
🧩 解题思路
这道题的本质是:在两个有序数组中,找到合并后第 k 小的元素 ,中位数就是特殊的第 k 小元素(总长度为奇数时取中间,偶数时取中间两个的平均)。
java
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int m = nums1.length;
int n = nums2.length;
int total = m + m;
//总长度为奇数: 找第 (total + 1)/2 小
//总长度为偶数: 找第 total/2 小 和 第(total/2)+1 小,再取平均
if(total % 2 == 1) {
return getk(nums1, nums2, 0, m-1, 0, n-1, (total+1)/2);
} else {
int k1 = total / 2;
int k2 = k1 + 1;
return (getk(nums1, nums2, 0, m-1, 0, n-1, k1) + getk(nums1, nums2, 0, m-1, 0, n-1, k2)) / 2.0;
}
}
// 参数含义:
// nums1, nums2:两个有序数组
// l1, r1:nums1 当前考虑的区间[l1, r1]
// l2, r2:nums2 当前考虑的区间[l2, r2]
int getk(int[] nums1, int[] nums2, int l1, int r1, int l2, int r2, int k) {
int len1 = r1 - l1 + 1; //num1 当前区间长度
int len2 = r2 - r2 + 1; //nums2 当前区间长度
//1. 让nums1 始终是更短的数组, 减少二分次数
if(len1 > len2) {
return getk(nums2, nums1, l2, r2, l1, r1, k);
}
//2. 边界情况: 如果一个数组已经空了, 直接返回另一个数组的第k小
if(len1 == 0){
return nums2[l2 + k - 1];
}
//3. 边界情况: k=1, 返回两个数组当前元素的较小值
if (k == 1) {
return Math.min(nums1[l1], nums2[l2]);
}
//4. 二分: 分别在两个数组中提取「前 k/2 个元素」的位置
//避免数组越界, 取 min(len1, k/2)
int idx1 = l1 + Math.min(len1, k/2) - 1;
int idx2 = l2 + Math.min(len2, k/2) - 1;
//5.比较两个数组中第k/2小的元素, 排除较小的一半
if (nums1[] > nums2[]){
//nums2 的前k/2 个元素一定都比第k小元素小, 排除他们
//新的k = k-被排除的元素个数(idx2 - l2 +1)
return getk(nums1, nums2, l1, r1,idx2 + 1, r2, k - (idx2 - l2 +1));
} else {
// nums1 的前 k/2 个元素一定都比第k小元素 小, 排除他们
return getk(nums1, nums2, ids1 + 1, r1, l2, r2, k - (idx1 - l1 + 1));
}
}
💡 核心逻辑拆解
1. 保证短数组优先 :if (len1 > len2) 交换两个数组,让 nums1 始终更短,减少后续二分的计算量。
2. 空数组处理 :如果一个数组区间为空,直接从另一个数组取第 k 小元素。
3. k=1 处理 :直接取两个数组当前区间首元素的较小值,就是第 1 小。
4. 二分排除:
- 每次在两个数组中取 k/2 长度的片段,比较片段末尾的元素。
- 较小的那个片段里的所有元素,一定都比第 k 小元素小,可以直接排除。
- 更新 k 和对应数组的区间起点,递归继续找。
✅ 示例运行
假设 nums1 = [1,3],nums2 = [2],总长度 = 3,中位数是第 2 小:
- 调用 getK(..., k=2)
- len1=2, len2=1 → 不交换
- k≠1,取 k/2=1
- idx1=0, idx2=0
- nums1[0]=1 < nums2[0]=2 → 排除 nums1 前 1 个元素
- 新调用:getK(nums1, nums2, 1, 1, 0, 0, 2-1=1)
- 新调用 k=1 → 返回 min(nums1[1]=3, nums2[0]=2) = 2
- 中位数就是 2,正确。
⚠️ 关键技巧
- 二分思想:每次排除 k/2 个元素,将问题规模减半,时间复杂度 O (log k) → 最终 O (log (min (m,n)))。
- 避免越界:用 Math.min(len, k/2) 保证取的位置不超过数组当前区间长度。
- 递归边界:空数组和 k=1 是递归的终止条件,保证不会无限递归。
如果还不理解题目建议可以去学学二分法, 可以看看我这篇文章 数据结构09(Java)-- 二分查找模板
小白啊!!!写的不好轻喷啊🤯如果觉得写的不好,点个赞吧🤪(批评是我写作的动力)
...。。。。。。。。。。。...

...。。。。。。。。。。。...