暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

编程题 - 找出两个有序数组的中位数

红牛编程 2019-11-07
183

找出两个有序数组的中位数

题目

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/median-of-two-sorted-arrays

给定两个大小为 m 和 n 的有序数组 nums1 和 nums2。

请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。

你可以假设 nums1 和 nums2 不会同时为空。

示例 1:

1nums1 = [1, 3]
2nums2 = [2]
3
4则中位数是 2.0

示例 2:

1nums1 = [1, 2]
2nums2 = [3, 4]
3
4则中位数是 (2 + 3)/2 = 2.5

中位数

先解释下一下中位数。

中位数(Median)又称中值,统计学中的专有名词,是按顺序排列的一组数据中居于中间位置的数,可将数值集合划分为相等的上下两部分, 即在这组数据中,有一半的数据比他大,有一半的数据比他小 。

二分查找

编程思路为:

题目要求的时间复杂度为 O(log(m+n))
,是使用二分法的典型复杂度。其实该方案的时间复杂度会是 O(log(min(m, n)))
因为我们对其中一个长度较短的数组做 二分查找即可(但这两个时间复杂度的描述差不多没有本质区别)。

核心问题在于判断当前的分割位置是否是将数据分为左右相等,一半小,一半大的序列。判断依据是若满足数组1左边最大的元素小于数组2右边最小的元素 同时 数组2左边最大的元素小于数组1右边最小的元素,那么说明分割正确。

步骤如下:

  1. 确定长度较短的数组,进行二分。

  2. 从数组中间进行分割,然后确定当前的分割标准是否满足中位数标准。

  3. 满足标准,分割完毕

  4. 不满足标准,确认应该向哪方移动分割点。

  5. 若数组1左边最大的元素大于数组2右边最小的元素,则向左移动

  6. 若数组2左边最大的元素大于数组1右边最小的元素,则向右移动

  7. 计算中位数

Go 代码实现

在 LeetCode 表现如下:

执行用时 :12 ms, 在所有 golang 提交中击败了96.87%的用户

内存消耗 :5.5 MB, 在所有 golang 提交中击败了79.21%的用户

 1import "math"
2
3func findMedianSortedArrays(nums1 []int, nums2 []int) float64 {
4    // 确定两个数组长度
5    l1, l2 := len(nums1), len(nums2)
6
7    // 保证二分元素数量少的数组
8    if l1 > l2 {
9        return findMedianSortedArrays(nums2, nums1)
10    }
11
12    //初始化变量
13    var (
14        leftMax1, leftMax2 int // 左边最大值,两个数组都要表示,用 1,2 区分
15        rightMin1, rightMin2 int // 右边最小值,两个数组都要表示,用 1,2 区分
16        cut1, cut2 int // 分割位置
17        begin = 0 // 二分当前窗口起始索引
18        end  = 2 * l1 // 二维当前窗口的结束索引
19    )
20
21    // 迭代二分
22    for begin <= end {
23        // nums1 二分
24        cut1 = (begin + end) / 2
25        // 利用 cut1 确定 cut2 的值
26        cut2 = l1 + l2 - cut1
27
28        // 若  cut1 == 0 意味着 数组1 的的全部元素都大中位数,那么就将 leftMax1 设置一个最小值,用于统计
29        // 反之 cut1 != 0 意味着 leftMax1 的值暂时定为 切割位置的元素值
30        if cut1 == 0 {
31            leftMax1 = math.MinInt64
32        } else {
33            leftMax1 = nums1[(cut1-1)/2]
34        }
35        // 若 cut1 == 2 * li 意味着 数组1 的全部元素都大于中位数,那么就另 rightMin1 设置为最大值,用于统计
36        // 反之 rightMin1 暂定为切割位置元素
37        if cut1 == 2 * l1 {
38            rightMin1 = math.MaxInt64
39        } else {
40            rightMin1 = nums1[cut1/2]
41        }
42        // 数组2 中的切割位置同理
43        if cut2 == 0 {
44            leftMax2 = math.MinInt64
45        } else {
46            leftMax2 = nums2[(cut2-1)/2]
47        }
48        if cut2 == 2 * l2 {
49            rightMin2 = math.MaxInt64
50        } else {
51            rightMin2 = nums2[cut2/2]
52        }
53
54        // 测试是否满足 数组1,2 的左边同时小于数组1,2 的右边,若不满足,移动二分窗口
55
56        // 若数组1 的左边存在比数组2 的右边大的数,则切割位置应该在当前切割位置的左边,因此开始不变,结束位置为当前切割位置
57        if leftMax1 > rightMin2 {
58            end = cut1 -1
59        } else  if leftMax2 > rightMin1 {
60            begin = cut1 + 1
61        } else {
62            break
63        }
64    }
65
66    // 找出切割位置左边最大值和右边最小值,求和/2 即可
67    leftMax := leftMax1
68    if leftMax1 < leftMax2 {
69        leftMax = leftMax2
70    }
71    rightMin := rightMin1
72    if rightMin1 > rightMin2 {
73        rightMin = rightMin2
74    }
75    return float64(leftMax + rightMin) / 2
76}

回复 problem,获取更多编程题讲解


文章转载自红牛编程,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论