给定两个大小分别为
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 先将两个数组有序合并至新数组中,然后根据奇偶情况返回中位数
- 方法2 先找到中位数可能的下标值 left 和 right,然后遍历数组得到 nums[left], nums[right],再根据奇偶情况返回中位数
- 方法3 二分法,题目要求 算法的时间复杂度应该为
O(log (m+n))
,此方法待研究 ...
时间复杂度:
- 方法1 O(m+n)
- 方法2 遍历
len/2 + 1
次,len=m+n
,所以时间复杂度依旧是 O(m+n) - 方法1
O(log (m+n))
空间复杂度:
- 方法1 O(m+n)
- 方法2 O(1)
- 方法3 O(1)
// 方法1 先将两个数组有序合并至新数组中,然后根据奇偶情况返回中位数
// 空间复杂度:O(m+n)
func findMedianSortedArrays(nums1 []int, nums2 []int) float64 {
i, j, length1, length2 := 0, 0, len(nums1), len(nums2)
newNums := make([]int, 0, length1 + length2)
for i, j = 0, 0; i < length1 && j < length2; {
if nums1[i] < nums2[j] {
newNums = append(newNums, nums1[i])
i++
} else {
newNums = append(newNums, nums2[j])
j++
}
}
if i < length1 {
newNums = append(newNums, nums1[i:]...)
}
if j < length2 {
newNums = append(newNums, nums2[j:]...)
}
// 1 2 3(2) 4 5
if (length1 + length2) % 2 != 0 {
return float64(newNums[(length1 + length2 - 1)/2])
}
// 1 2 3(2) 4(3) 5 6
return float64(newNums[(length1 + length2)/2] + newNums[(length1 + length2)/2 - 1]) / float64(2)
}
// 方法2 先找到中位数可能的下标值left和right,然后遍历数组得到nums[left],nums[right],再根据奇偶情况返回中位数
// 空间复杂度:O(1) 具体思路如下:
// 1.先根据nums1和nums2的数组长度,判断 奇数/偶数 情况,得到应返回的中位数下标left,right
// 奇数:left = right
// 偶数:left = right - 1
// 2.分别遍历nums1和nums2数组,每次找出nums1[i]和nums2[j]中的较小者。然后i,j和k(用来标记当前k是否正好是中位数的left/right下标)分别右移一位
// 如果k正好是中位数的left/right下标,找到其对应值,根据奇偶情况返回不同的res值即可
// 若是奇数情况,在找到了一个中位数下标后可直接返回;
// 若是偶数则当 k == right,说明已经找到了两个中位数,可提前结束循环并处理res值返回
func findMedianSortedArrays(nums1 []int, nums2 []int) float64 {
// 先找到奇数或偶数情况下,分别对应的中位数下标
l1, l2, left, right, isOdd := len(nums1), len(nums2), 0, 0, false
// 偶数:1 2(1); 3(2) 4【左1右1】 // 1; 2(1) 3(2) 4【右2】 // 1 2(1) 3(2); 4【左2】
if (l1+l2)%2 == 0 {
right = (l1 + l2) / 2
left = right - 1
isOdd = false
// 奇数:1 2 3(2); 4 5 ...
} else {
left = (l1 + l2) / 2
right = left // left和right一样
isOdd = true
}
i, j, k, tmp := 0, 0, 0, 0
var res float64
for i < l1 || j < l2 {
if i < l1 && j < l2 && nums1[i] <= nums2[j] {
tmp = nums1[i]
i++
} else if i < l1 && j < l2 && nums1[i] >= nums2[j] {
tmp = nums2[j]
j++
} else if i < l1 {
tmp = nums1[i]
i++
} else if j < l2 {
tmp = nums2[j]
j++
}
if k == left || k == right {
res += float64(tmp)
if isOdd || k == right { // 奇数直接返回结果,偶数则继续找第二个中位数
break
// return res
}
}
k++
}
if isOdd {
return res
}
return res / 2
}
// 上边的两种思路,时间复杂度都达不到题目的要求 O(log(m+n)。看到 log,很明显,我们只有用到二分方法才能达到。二分法待研究。。
// 该方法为上述方法2优化之前的代码,较为繁琐
// // func findMedianSortedArrays(nums1 []int, nums2 []int) float64 {
// // 先找到奇数或偶数情况下,分别对应的中位数下标
// l1, l2, left, right, isOdd := len(nums1), len(nums2), 0, 0, false
// // 偶数:1 2(1); 3(2) 4【左1右1】 // 1; 2(1) 3(2) 4【右2】 // 1 2(1) 3(2); 4【左2】
// if (l1+l2)%2 == 0 {
// right = (l1 + l2) / 2
// left = right - 1
// isOdd = false
// // 奇数:1 2 3(2); 4 5 ...
// } else {
// left = (l1 + l2) / 2
// right = left // left和right一样
// isOdd = true
// }
// // fmt.Printf("%v, %v, %v \n", isOdd, left, right)
// i, j, k, tmp := 0, 0, 0, 0
// var res float64
// for i < l1 && j < l2 {
// if nums1[i] < nums2[j] {
// tmp = nums1[i]
// i++
// } else {
// tmp = nums2[j]
// j++
// }
// if k == left || k == right {
// res += float64(tmp)
// if isOdd { // 奇数直接返回结果,偶数则继续找第二个中位数
// return res
// }
// }
// k++
// }
// // fmt.Printf("i=%v, j=%v, k=%v, l1=%v, l2=%v, left=%v, right=%v\n", i, j, k, l1, l2, left, right)
// // 如果nums1或nums2其中一个数组遍历完了,且中位数还未找完,则继续遍历另一个数组
// for i < l1 {
// if k == left || k == right {
// res += float64(nums1[i])
// // 奇数直接返回结果。偶数时,k已经等于第二个中位数的下标了,说明中位数都已经找完了
// if isOdd || k == right {
// break
// }
// }
// i++
// k++
// }
// for j < l2 {
// if k == left || k == right {
// res += float64(nums2[j])
// // 奇数则找到第一个中位数后直接返回,偶数则继续找第二个中位数
// if isOdd || k == right { // 奇数 或 偶数时,k已经等于第二个中位数的下标了
// break
// }
// }
// j++
// k++
// }
// if isOdd {
// return res
// }
// return res / 2
// }