zoukankan      html  css  js  c++  java
  • 寻找两个有序数组的中位数

    给定两个大小为 m 和 n 的有序数组 nums1 和 nums2。请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。你可以假设 nums1 和 nums2 不会同时为空。

    示例1:

    nums1 = [1, 3]
    nums2 = [2]

    则中位数是 2.0

    示例2:

    nums1 = [1,2]
    nums2 = [3,4]

    则中位数是 (2 + 3)/2 = 2.5

    题解:

    如果不考虑时间复杂度的话,最简单的做法,就是将nums1和nums2合并成一个数组并进行排序,然后取该数组的中位数,但是哪怕是快速排序,时间也为O(nlogn),明显与题目要求的O(log(m+n))不相符。所以,我们要另辟蹊径。而题目除了给出的两个数组,同时还告诉我们,这两个数组,是有序数组,这使得我们可以利用有序,在O(log(m+n))的时间复杂度内,寻得中位数。

    我们要知道,中位数的作用,是将数组的两端分成两个等长的片段,如:1 2 |3| 4 5或1 2 |3 4| 5 6,符号|将原数组除中位数外,分割成两个等长的片段。那么,现在我们有两个数组:num1: [a1,a2,a3,...an]和nums2: [b1,b2,b3,...bm],我们用|来分割数组[nums1[:i],nums2[:j] | nums1[i:], nums2[j:]],只要能保证经由|分割出来的两个片段是等长的,中位数就在|这个边界产生。

    首先,我们来做一个假设,我们假设nums1的长度永远小于或者等于nums2,那么,nums2至少包含一个中位数。其次,我们假定nums3是nums1和nums2合并后并按照顺序排列,如果len(nums3)的长度为奇数,那么中位数的位置则为(len(nums3)+1)/2,如果len(nums3)为偶数,那么中位数的位置有两个,len(nums3)/2和(len(nums3)/2)+1,而在计算机的世界里,任何整数除以2,都会向下取整,比如整数6和7除以2的结果都是3,为了方便表示,我们统一认为中位数会在(len(nums3)+1)/2的位置上出现,即(len(nums1)+len(nums2)+1)/2。

    比如:

    nums3 = [1,2,3,4,5,6,7],那么中位数处于m=(7+1)/2=4

    或者nums3=[1,2,3,4,5,6],那么中位数处于m1=(6+1)/2=3,m2=m1+1=4

    我们看下面的表格,我们假定nums1的索引i和nums2的索引j处于|的两端,len(left)=len(right)且max(ai,bj)<=min(ai+1,bj+1)。

    left right
    [a1,a2……ai],[b1,b2,……bj] [ai+1,……an],[bj+1,……bm]

    如果我们能确定i在nums1中的位置,那么同样可以确定j在nums2的位置,j=(len(nums1)+len(nums2)+1/2)-i。

    现在,我们就可以开始着手写程序了:

    //根据index获取nums中的值,如果index无效,则返回defaultVal
    func GetValWithDefault(nums []int, index int, defaultVal int) int {
    	if index >= 0 && index < len(nums) {
    		return nums[index]
    	}
    	return defaultVal
    }
    func findMedianSortedArrays(nums1 []int, nums2 []int) float64 {
    	if len(nums1) > len(nums2) { //<1>如果nums1的长度大于nums2,则替换引用,确保nums2中至少存在一个中位数
    		nums1, nums2 = nums2, nums1
    	}
    	l1, l2 := len(nums1), len(nums2)
    	left, right := 0, l1
    	m := (l1 + l2 + 1) / 2
    	for left < right {
    		i := left + (right-left)/2 //<2>寻找i和j,使得[nums1[:i],nums2[:j] | nums1[i:], nums2[j:]]能将两个数组切割成两个等长的片段
    		j := m - i
    		if nums1[i] < nums2[j-1] {
    			left = i + 1
    		} else {
    			right = i
    		}
    	}
    	right = m - left //<3>定位到j的位置
    	maxLeft := math.Max(
    		float64(GetValWithDefault(nums1, left-1, math.MinInt32)),
    		float64(GetValWithDefault(nums2, right-1, math.MinInt32)),
    	)
    	if (l1+l2)%2 == 1 {
    		return maxLeft
    	}
    	minRight := math.Min(
    		float64(GetValWithDefault(nums1, left, math.MaxInt32)),
    		float64(GetValWithDefault(nums2, right, math.MaxInt32)),
    	)
    	return (maxLeft + minRight) / 2
    }
    

      

    按照上述代码,我们可以将时间复杂度控制在log(min(m,n))之内。

  • 相关阅读:
    让eclipse中选中的变量以指定颜色高亮显示
    IDirect3DDevice9::DrawPrimitive
    3D中的切线空间简介(转)
    Scenario 3: Drawing One Triangle with Indexing
    Scenario 2: Drawing Two Triangles with Indexing
    aswing学习笔记
    aswing学习笔记4通过调用面板中的按钮实现主界面动态切换皮肤的问题!
    flexBuilder3中生成的模板页不支持flash全屏的修改办法
    Eclipse和FlexBuilder中设置编辑代码高亮
    aswing学习笔记2不规则外框请教思路
  • 原文地址:https://www.cnblogs.com/beiluowuzheng/p/11145412.html
Copyright © 2011-2022 走看看