给定两个大小分别为 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
1 <= m + n <= 2000
-106 <= nums1[i], nums2[i] <= 106
### 解题思路详解
1. **基本概念**:
- 如果将两个数组合并,中位数的位置取决于合并后数组的总长度(\(m+n\))。
- 对于奇数长度,中位数是中间的元素;对于偶数长度,中位数是中间两个元素的平均值。
2. **处理不同长度的数组**:
- 为了方便处理,始终让较短的数组为 `nums1`,这样可以减少需要处理的可能的分割位置,优化性能。
3. **计算中位数位置**:
- 定义 `totalLeft =(m+n+1)/2`。这是合并后数组左侧部分应包含的元素数量。
4. **二分搜索策略**:
- 在 `nums1` 中使用二分搜索来确定一个索引 `i`,`nums2` 的索引 `j` 则由 `i` 自动确定:`j = totalLeft - i`。
- 这样,`nums1[0...i-1]` 和 `nums2[0...j-1]` 构成了合并后数组的左半部,而 `nums1[i...m-1]` 和 `nums2[j...n-1]` 构成了右半部。
5. **保持左半部大于等于右半部**:
- 确保 `nums1[i-1] <= nums2[j]` 和 `nums2[j-1] <= nums1[i]`,这样左半部的所有元素都不大于右半部的任何元素。
6. **调整二分搜索的边界**:
- 如果 `nums1[i-1] > nums2[j]`,说明 `i` 太大,需要减小 `i`,即调整二分搜索的上界。
- 如果 `nums2[j-1] > nums1[i]`,说明 `i` 太小,需要增大 `i`,即调整二分搜索的下界。
7. **计算中位数**:
- 在找到满足条件的 `i` 和 `j` 后:
- 如果总长度是奇数,中位数是 `max(nums1[i-1], nums2[j-1])`。
- 如果总长度是偶数,中位数是 `(max(nums1[i-1], nums2[j-1]) + min(nums1[i], nums2[j])) / 2.0`。
让我们通过一个具体的样例来详细解释如何使用二分法找到两个已排序数组的中位数。假设有两个数组如下:
- `nums1 = [1, 3, 8]`
- `nums2 = [7, 9, 10, 11]`
### 初始设置
数组长度分别为 `m = 3` 和 `n = 4`,因此合并后的数组长度为 `m + n = 7`。根据合并后数组长度,我们可以计算:
- `totalLeft = (m + n + 1) / 2 = (3 + 4 + 1) / 2 = 4`。
这意味着在找到中位数时,合并后数组左半部分需要包含4个元素。
### 二分搜索过程
我们在较短的数组 `nums1` 上执行二分搜索。设定初始边界 `l = 0` 和 `r = m = 3`。
1. **第一次迭代**:
- `i = (l + r) / 2 = (0 + 3) / 2 = 1.5` 向下取整为 `1`。
- 对应的 `j = totalLeft - i = 4 - 1 = 3`。
- 检查 `nums1[i-1] <= nums2[j]` 和 `nums2[j-1] <= nums1[i]`:
- `nums1[0] = 1 <= nums2[3] = 11` ✓
- `nums2[2] = 10 <= nums1[1] = 3` ✗
- `10 > 3`,`i` 太小,需要向右调整。设置 `l = i + 1 = 2`。
2. **第二次迭代**:
- `i = (l + r) / 2 = (2 + 3) / 2 = 2.5` 向下取整为 `2`。
- `j = totalLeft - i = 4 - 2 = 2`。
- 检查:
- `nums1[1] = 3 <= nums2[2] = 10` ✓
- `nums2[1] = 9 <= nums1[2] = 8` ✗
- `9 > 8`,`i` 仍然太小,向右调整。设置 `l = i + 1 = 3`。
3. **第三次迭代**:
- `i = (l + r) / 2 = (3 + 3) / 2 = 3`。
- `j = totalLeft - i = 4 - 3 = 1`。
- 检查:
- `nums1[2] = 8 <= nums2[1] = 9` ✓
- `nums2[0] = 7 <= nums1[3] = INT_MAX` (因为 `nums1[3]` 越界) ✓
- 检查通过,停止搜索。
### 计算中位数
由于 `m + n = 7` 是奇数,我们只需要找到合并后数组左半部分的最大值:
- `maxLeft = max(nums1[i-1], nums2[j-1]) = max(nums1[2], nums2[0]) = max(8, 7) = 8`。
因此,中位数是 `8`。
通过这道题的学习得到的具体的启发:
1. 金融数据处理
在金融行业,实时数据流的分析非常重要,如股票价格、交易量等。使用类似二分搜索的算法可以快速地从大量历史数据中找到中位数或其他统计量,帮助进行市场趋势分析或决策支持。
启发:
- 使用高效的算法可以实时处理和分析大规模数据,帮助金融分析师快速做出投资决策。
2. 电子商务平台的价格优化
电商平台需要处理和分析大量的商品价格信息来调整其销售策略。通过快速确定商品价格的中位数,可以帮助平台调整定价策略,保持竞争力。
启发:
- 算法可以用于动态定价系统,实时调整价格以优化销售和利润。
3. 大数据环境下的资源分配
在大数据和云计算环境中,资源如计算力和存储的合理分配是优化性能和成本的关键。通过分析资源使用的中位数,可以更合理地规划资源分配。
启发:
- 针对资源使用的模式分析,可以提高资源利用率,降低成本。
4. 医疗数据分析
在医疗领域,快速从患者的历史健康记录中提取关键统计数据(如中位数体温、血压等)对于诊断和治疗计划制定非常重要。
启发:
- 高效的数据处理算法可以提高医疗服务的响应速度和准确性,改善患者治疗结果。
5. 网络安全中的异常检测
在网络安全领域,需要快速分析大量数据包或交易记录以检测潜在的异常或攻击行为。使用算法快速找到交易额或数据流的中位数,可以帮助识别异常模式。
启发:
- 实时数据分析和处理能力是有效的安全监控的基石,有助于及时发现并阻止安全威胁。
6. 推荐系统
推荐系统需要分析用户的行为数据,确定用户喜好的中位数标准,以提供更加个性化的推荐。
启发:
- 精确快速的数据处理能力可以提高推荐系统的准确率和用户满意度。