一、问题背景与算法选择
在iOS开发面试或算法竞赛中,合并两个有序数组并求解中位数是高频考点。该问题要求在O(log(m+n))时间复杂度内完成,但本文将通过三种基础排序算法的实现,帮助开发者理解不同算法的适用场景与优化空间。
1.1 问题定义
给定两个非递减顺序的整数数组nums1和nums2,合并后找到新数组的中位数。若合并后长度为偶数,则返回中间两个数的平均值。
1.2 算法选择依据
- 冒泡排序:适合教学演示,但时间复杂度较高(O(n²))
- 选择排序:通过每次选择最小值优化比较次数
- 插入排序:对小规模有序数据效率较高
二、冒泡排序实现方案
2.1 算法原理
通过相邻元素比较交换,将较大值逐步”冒泡”至数组末端。合并两个数组后执行冒泡排序,最后根据数组长度奇偶性返回中位数。
2.2 Swift代码实现
func findMedianSortedArraysBubble(_ nums1: [Int], _ nums2: [Int]) -> Double {var merged = nums1 + nums2let n = merged.count// 冒泡排序for i in 0..<n {for j in 0..<n-i-1 {if merged[j] > merged[j+1] {merged.swapAt(j, j+1)}}}// 计算中位数if n % 2 == 0 {return Double(merged[n/2 - 1] + merged[n/2]) / 2.0} else {return Double(merged[n/2])}}
2.3 性能分析
- 时间复杂度:O((m+n)²)
- 空间复杂度:O(m+n)(需存储合并数组)
- 适用场景:教学演示或极小规模数据
三、选择排序优化方案
3.1 算法改进点
通过每次循环选择两个数组中的最小值,避免全量排序。使用双指针遍历两个数组,动态维护当前最小值。
3.2 Swift代码实现
func findMedianSortedArraysSelection(_ nums1: [Int], _ nums2: [Int]) -> Double {var merged = [Int]()var i = 0, j = 0let total = nums1.count + nums2.count// 合并数组(选择排序思想)while merged.count < (total + 1) / 2 {if i < nums1.count && (j >= nums2.count || nums1[i] <= nums2[j]) {merged.append(nums1[i])i += 1} else {merged.append(nums2[j])j += 1}}// 计算中位数if total % 2 == 0 {let next = merged.last!let prev = (i > 0 && j > 0) ? min(nums1[i-1], nums2[j-1]) :(i > 0 ? nums1[i-1] : nums2[j-1])return Double(prev + next) / 2.0} else {return Double(merged.last!)}}
3.3 优化效果
- 时间复杂度:O(m+n)
- 空间复杂度:O((m+n)/2)(仅存储前半部分)
- 改进点:减少不必要的完整排序
四、插入排序的渐进优化
4.1 算法特性
对已部分有序的数据效率较高,可通过二分查找优化插入位置。本方案采用双指针+插入排序的混合策略。
4.2 Swift代码实现
func findMedianSortedArraysInsertion(_ nums1: [Int], _ nums2: [Int]) -> Double {var merged = [Int]()var i = 0, j = 0// 双指针合并(插入排序思想)while i < nums1.count || j < nums2.count {if i >= nums1.count {merged.append(nums2[j])j += 1} else if j >= nums2.count {merged.append(nums1[i])i += 1} else if nums1[i] <= nums2[j] {merged.append(nums1[i])i += 1} else {merged.append(nums2[j])j += 1}}// 计算中位数(优化版)let mid = merged.count / 2return merged.count % 2 == 0 ?Double(merged[mid-1] + merged[mid]) / 2.0 :Double(merged[mid])}
4.3 性能对比
| 算法 | 时间复杂度 | 空间复杂度 | 适用场景 |
|---|---|---|---|
| 冒泡排序 | O(n²) | O(n) | 教学演示 |
| 选择排序优化 | O(m+n) | O(n/2) | 中等规模有序数据 |
| 插入排序优化 | O(m+n) | O(m+n) | 需要稳定排序的场景 |
五、工程化实践建议
-
算法选择策略:
- 小规模数据(n<100):插入排序
- 中等规模数据:选择排序优化
- 超大规模数据:考虑二分查找+双指针(O(log(m+n))方案)
-
Swift语言优化:
// 使用Swift特有语法优化func optimizedMerge(_ nums1: [Int], _ nums2: [Int]) -> Double {var merged = nums1 + nums2merged.sort() // 使用Swift内置排序(Timsort变种)let count = merged.countlet mid = count / 2return count % 2 == 0 ?(Double(merged[mid-1]) + Double(merged[mid])) / 2 :Double(merged[mid])}
-
测试用例设计:
func testMedianAlgorithms() {let testCases = [([1, 3], [2], 2.0),([1, 2], [3, 4], 2.5),([0, 0], [0, 0], 0.0),([], [1], 1.0),([2], [], 2.0)]testCases.forEach { (nums1, nums2, expected) inassert(findMedianSortedArraysBubble(nums1, nums2) == expected)assert(findMedianSortedArraysSelection(nums1, nums2) == expected)assert(findMedianSortedArraysInsertion(nums1, nums2) == expected)}}
六、进阶方向
-
O(log(m+n))算法实现:
- 采用二分查找定位中位数位置
- 通过比较两个数组的中间元素动态调整搜索范围
-
通用化扩展:
- 支持K个有序数组的中位数求解
- 添加权重参数实现加权中位数计算
-
性能监控:
func benchmarkAlgorithm(_ algorithm: ([Int], [Int]) -> Double,_ nums1: [Int], _ nums2: [Int]) {let start = DispatchTime.now()let _ = algorithm(nums1, nums2)let end = DispatchTime.now()let nanoTime = end.uptimeNanoseconds - start.uptimeNanosecondslet timeInterval = Double(nanoTime) / 1_000_000_000print("耗时: \(timeInterval)秒")}
本文通过三种算法实现方案,系统展示了iOS开发中处理有序数组合并问题的不同思路。开发者可根据实际场景选择合适方案,同时建议掌握二分查找的终极优化方法以应对大规模数据挑战。