4 两个有序数组中第k小的数

两个有序数组中第k个元素

转载请标明出处(http://blog.csdn.net/lis_12/article/details/53128594).

题目

求两个有序数组中第k个元素。要求时间复杂度O(log(m+n)) ,空间复杂度O(1)。

解决方法

方案1

def median(num1,num2,k):
    len1,len2 = len(num1),len(num2)

    if (len1 + len2) < k:#首先得检查合法行啊
        return -1

    index1,index2 = 0,0
    while index1 < len1 and index2 < len2:
        if index1 + index2 == k - 1:  #此时已经比较了k-1个,所以此时较小的那个就是第k个小的数
            return min(num1[index1],num2[index2])

        if num1[index1] < num2[index2]:
            index1 += 1
        else:
            index2 += 1

        if index1 >= len1 or index2 >= len2:#防止越界
            break

    #num2空了但是未取到第k个数,所以num1中第index1 + k - index2 - 1就为第k个数(-1是因为索引从0开始)
    if (index1 + index2) < k and index1 < len1:
        return num1[k - index2 - 1]
    #同理
    if (index1 + index2) < k and index2 < len2:
        return num2[k - index1 - 1]

这种方法的时间复杂度为O(m + n),不合要求啊。看到(log(m+n))就会想到二分。。。

方案2,利用二分查找

假设有序序列 A和B的元素个数都大于k/2,下标从0开始,A[x]代表A的第x+1个元素,总序列是由A,B组成的有序序列,寻找总序列中的第k个元素。

比较A[k/2-1]和B[k/2-1]的值,

A[k/2-1]:A中第k/2个元素;

B[k/2-1]:B中第k/2个元素;

这两个元素比较共有三种情况:

  1. 如果A[k/2-1] < B[k/2-1],这表明A[0]至A[k/2-1] (包括A[k/2-1])的元素都在A和B合并之后的总序列中的前k个元素中。也就是说,A[0]至A[k/2-1]不可能是总序列的第k个值,可以将其抛弃;
  2. 同理,如果A[k/2-1] > B[k/2-1],这表示B[0]至B[k/2-1]的元素都在A和B合并之后的前k个元素中,可以抛弃;
  3. 如果A[k/2-1] == B[k/2-1],运气很好啊,说明已经找到第k个元素了,直接返回。因为两个数组中分别有k/2 - 1个元素小于A[k/2-1],并且序列都有序,那么A[k/2 - 1]肯定是第k个元素了。

举例说明:

A = (A0,A1,A2,A3);B = (B0,B1,B2,B3);A0 < A1 < A2 < A3,B0 < B1 < B2 < B3.寻找总序列的第4个元素。

分别取A和B中的第2个元素,A1,B1;

  1. 如果A1 < B1,说明A0,A1不可能是总序列的第4个元素,舍弃A0,A1;

    因为A0,A1,B0,B1中B1最大,并且序列有序,A0,A1不可能是总序列的第k个元素,所以可以舍弃A0,A1;

  2. 如果A1 > B1,说明B0,B1不可能是总序列的第4个元素,舍弃B0,B1;

    因为A0,A1,B0,B1中A1最大,并且序列有序,B0,B1不可能是总序列的第k个元素,所以可以舍弃B0,B1;

  3. 如果A1 = B1,说明A1恰好为总序列的第k个元素,因为A0,B0比A1,B1小,总序列排序:min(A0,B0) <= max(A0,B0) <= A1<=B1,所以A1恰好为总序列的第4个元素。


由以上可知,可以通过二分法将问题规模缩小,假设p + q = k,A[p -1]对应A中第p个元素,总序列是由A,B组成的有序序列

  1. 如果序列A中的第p个元素小于序列B中的第q个元素,则序列A的前p个元素肯定都小于总序列的第k个元素,即序列A中的前p个元素肯定不是总序列的第k个元素,所以将序列A的前p个元素全部抛弃,形成一个较短的新序列;然后,用新序列替代原先的A序列,再找其中的第k - p个元素(因为已经排除了p个元素,k需要更新为k-p),依次递归;
  2. 同理,如果A序列中的第p个元素大于序列B中的第q个元素,则抛弃序列B的前q个元素,k = k - q;
  3. 如果序列A的第p个元素等于序列B的第q个元素,则第k个元素为A(p-1)

递归终止条件:

  1. 如果一个序列为空,那么第k个元素就是另一个序列的第k个元素;

  2. 如果k = 1,那么直接返回min(A[0],B[0])

  3. 如果A[p - 1] == B[q - 1],则第k个数就为A[p - 1];

根据以上就可以写代码啦- -

def fun(nums1,nums2,k):
    if (len(nums2) + len(nums1)) < k:  #防患于未然
        return 'error,k大于两个list总个数'

    #必须优先检查list是否为空,不然会越界...
    #如果nums2为空,那么第k个元素,肯定是nums[k - 1]了啊(k - 1为序列第k个元素,是因为list从0开始...)
    if len(nums2) == 0:
        return nums1[k - 1]

    #为了防止越界,保证nums1长度大于nums2
    if len(nums2) > len(nums1):
        return self.fun(nums2,nums1,k)

    #取第一个元素,肯定是要比两个list的首个元素中比较小的那个
    if k == 1:
        return min(nums1[0],nums2[0])

    #防不胜防啊...因为nums2比nums1短,取k/2有可能越界...,检查了n久没查出来...
    #如果nums2个数不足k/2,所以只能取比k/2小,又不越界的最大值了,即len(nums2),只要保证p + q = k就好啦...
    q = min(k / 2, len(nums2))
    p = k - q
    if nums1[p - 1] < nums2[q - 1]:
        return self.fun(nums1[p:],nums2,k - p)
    elif nums1[p - 1] > nums2[q - 1]:
        return self.fun(nums1,nums2[q:],k - q)
    else:
        return nums1[p - 1] #返回nums2[q - 1]也可以,反正一样大....

参考网址

  1. http://blog.csdn.net/yutianzuijin/article/details/11499917/
  2. http://www.07net01.com/2015/07/871155.html
  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值