让你详细理解寻找两个有序数组第k个数的递归算法

寻找两个有序数组中的第k个数

假设有两个数组A与B

A为[1,2,5,6.7]

B为[1,2,3,4,5]

现在需要寻找A、B合并之后的数组中的第4个数

方法一

比较偷懒的方法

直接使用C++中的merge函数将A与B合并为一个有序数组再按照顺序进行查找,时间复杂度为O(n+m)

方法二

使用二分法进行查找

我们首先需要明确目标,是在有序数组(尽管查找之前需要合并)中寻找第k个数,既然是有序数组,我们就想要利用有序这个特性,也就会自然想到二分查找算法。

但是现在的障碍是我们无法直接一下直接使用二分查找,因为是两个数组。

我们可以先画出A和B的图,如下所示

在这里插入图片描述

**我们的目标是找到第k个数,但是我们可以先找到这个k个数里面的k/2个数 **

(注意:此处数组的下标为k/2-1是因为 从0到k/2-1有k/2个数)

假设A[k/2-1]<B[k/2-1],我们可以得出A[0]—A[k/2-1]的这些数字一定都在前k个数字当中。因为如果说A[k/2-1]恰好等于B[k/2-1],第k个数字肯定就是A[k/2-1]或者B[k/2-1],但是现在A[k/2-1]<B[k/2-1],因此前k个数字当中一定有k/2个数是A[0]-A[k/2-1]的这些数,我们此时不知道前k/2个数究竟是不是就是A[0]-A[k/2-1],也无需考虑这些,只要知道前k个数字中我们已经找到了一半的数字,剩下的工作就是再找剩下的一半数字

那么我们只需要将寻找的范围缩小到剩下的数字即可,但是我每次查找的时候只查找剩下个数的一半

对于A的再次寻找范围为:A[k/2]—A[k/2+(k/2/2)-1] (此处的k/2+(k/2/2)-1是因为每次我只查找剩下需要查找个数的一半,同时k/2+(k/2/2)-1到k/2有k/2/2个数字)

对于B的再次寻找范围为:B[0]—B[k/2/2-1]

假设A[k/2+(k/2/2)-1]<B[k/2/2-1],那么和上面一样,我又可以再次找到属于前k个数当中的k/2/2个数字。

那么我只剩下了k/2/2个数字需要查找了。

如此递归下去直到需要查找的数字只剩下一个的时候,那么这个数字必定就是我们需要找到的第k个数字

为什么呢?因为我们之前找到的数字都是小于k的。

为什么都是小于k的呢?因为我们从来没有将比较大的那个数字划分到前k个数字当中。比如说第一次寻找k/2个数字的时候,我们并没有将较大的B[k/2-1]加入到已经找到的数字当中,而是将A[0]—A[k/2-1]这个k/2个数字加入到已经找到数字当中。第二次寻找k/2/2个数字的时候同样没有将较大的B[k/2/2-1]加入到已经找到的数字当中,而是将A[k/2]-A[k/2+k/2/2-1]加入到已经找到的数字当中。

从而当最后只剩下一个数字需要寻找的时候,那个数字必然就是第k个数字。

代码如下

int findKthNumber(const vector<int> &v1,int v1Start,const vector<int> &v2,int v2Start,int k){
    
    //当v1元素个数比较少的时候,一直+会导致其start超过其size,那我只需要返回我此轮需要找到的个数即可,比如说我此轮还差两个就满k个数字了,那我就返回此轮v2的start之后的第二个数字就行了
    if(v1Start>=v1.size()){
        return v2[v2Start+k-1];
    }
    //同上
    if(v2Start>=v2.size()){
        return v1[v1Start+k-1];
    }

    //跳出递归的条件:只剩下一个数字需要寻找
    if(k==1){
        return v1[v1Start]<=v2[v2Start]?v1[v1Start]:v2[v2Start];
    }

    //此处的INT_MAX主要防止数组元素个数比较少但此时该数字还未遍历完全,在寻找的时候在+k/2-1之后超过本身的size,如果超过本身的size,那我直接从另外一个数组寻找此轮需要的个数就行
    int v1Key = v1Start+k/2-1<v1.size()?
                v1[v1Start+k/2-1]:INT_MAX;
    int v2Key = v2Start+k/2-1<v2.size()?
                v2[v2Start+k/2-1]:INT_MAX;

    //关键:寻找的个数一直在变少!!!
    if(v1Key<v2Key){
        return findKthNumber(v1,v1Start+k/2,v2,v2Start,k-k/2);
    }
    else
    {
        return findKthNumber(v1,v1Start,v2,v2Start+k/2,k-k/2);
    }
    
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值