面试中算法(找到两个数组的中位数)

有两个升序数组,如何找出这两个数组归并以后新的升序数组的中位数?

中位数把一个升序数组分成了长度相等的两部分,其中左半部分的最大值永远小于或等于右半部分的最小值。

如上图所示,对于偶数长度的数组,可以根据中位数分成长度相等的两部分,左半部分的最大元素(6),永远小于或等于右半部分的最小元素(7)。

如上图所示,对于奇数长度的数组,如果把中位数本身归入左半部分,则左半边长度=右半边长度+1。 左半部分的最大元素(5),永远小于或等于右半部分的最小元素(6)。

假设:

数组A的长度是m,绿色和橙色元素的分界点是i,

数组B的长度是n,绿色和橙色元素的分界点是j,

那么为了让大数组的左右两部分长度相等,则i和j需要符合如下两个条件:

i+j= (m+n+1)/2 (之所以m+n后面+1,是为了应对大数组长度为奇数情况。)

Max(A[i-1],Blj-1])<= Min(A[i], B[j])(就是最大的绿色元素小于或等于最小的橙色元素。)

由于m+n的值是恒定的,所以我们只要确定一个合适的i,就可以确定j,从而找到大数组左半部分和右半部分的分界,也就找到了归并之后大数组的中位数。

其中i可以利用“二分查找”的思想。 

如下图所示:

第一步:就如二分查找那样,把i设在数组A的正中位置 i=3:

第二步:根据i的值来确定j的值,j= (m+n+1)/2 -i = 5:

 

 第三步: 验证i和j,分为下面三种情况:

1、B[j-1]≤A[i]&& A[i-1]≤B[j] 说明i和j左侧的元素都小于或等于右侧的元素。

2、A[i]<B[j-1] 说明i对应的元素偏小了,i应该向右侧移动。

3、A[i-1]>B[j] 说明i-1对应的元素偏大了,i应该向左侧移动。

显然,图中的属于情况2,A[3]< B[5],所以i应该向右移动。

第四步:在数组A的右半部分,重新确定i的位置,就像二分查找一样:

第五步:同第二步,根据i的值来确定j的值,j= (m+n+1)/2-i = 3: 

 

第六步:同第三步,验证i和j: 由于A[5] >=B[2]且B[3]>=A[4],所以这一组i和j是合适的!

第七步:找出中位数:

如果大数组的长度是奇数

中位数= Max (A[i-1],B[j-1]>)   (大数组左半部分的最大值。)

如果大数组的长度是偶数

中位数= (Max (A[i-1],B[j-1]) +Min(A[i],B[i]))/2   

(大数组左半部分的最大值和大数组右半部分的最小值取平均。)

在图示:大数组的长度是奇数,所以中位数=Max (8,12)=12。

特殊情况:

1、数组A的长度远大于数组B 

     可以提前把数组A和数组B进行交换,较短的数组放在前面,i从较短的数组中取。由于数组A是较短数组,i的搜索次数减少了。

2、无法找到合适的i值

 (1)数组A的长度<数组B的长度,并且数组A的所有元素都>数组B的元素。

可以跳出二分查找的循环,所求的中位数是B[j-1]。(仅限奇数情况) 

(2)数组A的长度<数组B的长度,并且数组A的所有元素都<数组B的元素。

可以跳出二分查找的循环,所求的中位数是Max (A[i-1],B[j-1])。(仅限奇数情况)

def get_middle_number(arrA,arrB):
    #获取数组的大小
    m,n=len(arrA),len(arrB)
    #判断arrA大小>=arrB大小,则交换
    if m>n:
        arrA,arrB, m,n,=arrB,arrA,n,m
    #如果n为0,值异常
    if n==0:
        raise  ValueError
    #起点,终点,中间
    start,end,half=0,m,(m+n+1)//2
    #循环
    while start<=end:
        i=(start+end)//2
        j=half-i
        # 验证i和j,三种情况
        if i<m and arrB[j-1]>arrA[i]:
            #(1) i偏小了,需要右移
            start=i+1
        elif i>0 and arrA[i-1]>arrB[j]:
            #(2) i偏大了,需要左移
            end=i-1
        else:
            #(3) i刚好合适,或i已达到数组边界
            if i == 0:
                max_of_left = arrB[j - 1]
            elif j == 0:
                max_of_left = arrA[i - 1]
            else:
                max_of_left = max(arrA[i - 1], arrB[j - 1])
            if (m + n) % 2 == 1:
                # 如果大数组的长度是奇数,中位数就是左半部分的最大值
                return max_of_left
            if i == m:
                min_of_right = arrB[j]
            elif j == n:
                min_of_right = arrA[i]
            else:
                min_of_right = min(arrA[i], arrB[j])
            # 如果大数组的长度是偶数,取左侧最大值和右侧最小值的平均
            return (max_of_left + min_of_right) / 2.0

if __name__ == '__main__':
    arrA = list([3, 5, 6, 7, 8, 15, 20])
    arrB = list([1, 10, 12, 18,21,24,25,29])
    print(get_middle_number(arrA,arrB))

  • 19
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
寻找两个正序数组中位数可以使用归并的方式,合并两个有序数组,得到一个大的有序数组。然后找到大的有序数组间位置的元素,即为中位数。另一种方法是使用双指针的方式,维护两个指针,初始时分别指向两个数组的下标0的位置。每次将指向较小值的指针后移一位(如果一个指针已经到达数组末尾,则只需要移动另一个数组的指针),直到到达中位数的位置。这样可以在O(log(m+n))的时间复杂度内找到中位数。 具体步骤如下: 1. 初始化指针p1和p2分别指向两个数组的起始位置0。 2. 判断两个指针所指的元素大小,较小的元素所在的指针后移一位,直到其一个指针到达数组末尾。 3. 若两个数组的长度之和为奇数,那么中位数即为当前指针指向的元素; 若两个数组的长度之和为偶数,那么中位数为当前指针指向的元素与其下一个元素的平均值。 4. 返回中位数作为结果。 需要注意的是,为了保证时间复杂度为O(log(m+n)),在每次移动指针时,应该移动的步数应该是当前指针所在数组长度的一半,即k/2,其k为两个数组的长度之和。 以下是一个示例代码,用于说明上述方法的实现: ``` int findMedianSortedArrays(int[] nums1, int[] nums2) { int m = nums1.length; int n = nums2.length; int total = m + n; int middle = total / 2; int p1 = 0, p2 = 0; int prev = 0, curr = 0; for (int i = 0; i <= middle; i++) { prev = curr; if (p1 < m && (p2 >= n || nums1[p1 < nums2[p2])) { curr = nums1[p1++]; } else { curr = nums2[p2++]; } } if (total % 2 == 0) { return (prev + curr) / 2; } else { return curr; } } ``` 该方法可以在O(log(m+n))的时间复杂度内找到两个正序数组中位数。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [算法:寻找两个正序数组中位数。](https://blog.csdn.net/en_joker/article/details/107179641)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [寻找两个正序数组中位数](https://blog.csdn.net/wulila/article/details/124483500)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值