两个有序数序列中找第k小

  Description:
已知两个已经排好序(非减序)的序列X和Y,其中X的长度为m,Y长度为n,

现在请你用分治算法,找出X和Y的第k小的数,算法时间复杂度为O(max{logm, logn})

分析:

既然说明了分治,那肯定是划分为子问题。又已经排好序,类比于二分查找,每次丢弃一半,我们可以将X的一半元素与Y的一半元素合并在一起,

再判断丢弃哪部分。取X[xBeg...xMid], Y[yBeg...yMid],合在一起的长度为halfLen

若 X[xMid] < Y[yMid],X[xBeg...xMid]一定小于Y[yMid]。若 k < halfLen,说明Y[yMid...yEnd]必定不可能出现第k小,丢弃。若 k >= halfLen,

说明X[xBeg...xMid]不可能出现第k小,因为X[xMid] < Y[yMid],极端情况下也只能是k-1小。所以丢弃X的左半段,此时k值发生改变,不再是找第k小,

而是 k-(xMid-xBeg+1)小。

同理X[xMid] >= Y[yMid]

递归边界的确定:

一半一半地丢弃下去,总会导致X或Y某一段不存在。所以递归边界为:

if(xBeg > xEnd) return Y[yBeg-k+1];

if(yBeg > yEnd) return X[xBeg-k+1];

#include <iostream>

using namespace std;

int find(int *x, int *y, int xbeg, int xend, int ybeg, int yend, int k){
  if(xbeg > xend){
    return y[ybeg + k - 1];
  }
  if(ybeg > yend){
    return x[xbeg + k -1];
  }

  int xmid = (xbeg + xend)/2;
  int ymid = (ybeg + yend)/2;
  int halfLen = xmid - xbeg + ymid - ybeg + 2;

  if(x[xmid] < y[ymid]){//在合并的数组中,原X[xbeg...xmid]所有元素一定在Y[ymid]的左侧
    if(k < halfLen){// y的右半段不需要寻找了
     return find(x, y, xbeg, xend, ybeg, ymid-1, k);
    }else{// x的左半段不需要寻找了,因为X[xmid]<Y[ymid],所以极端情况x[xmid]也只能是k-1小的元素,所以取xmid+1到xend
     return  find(x, y, xmid+1, xend, ybeg, yend,  k-(xmid-xbeg+1));//注意k值的改变
    }
  }else{//在合并的数组中,原Y[yBeg...yMid]的所有元素一定在X[xMid]的左侧
    if(k < halfLen){
     return find(x, y, xbeg, xmid-1, ybeg, yend, k);
    }else{
     return find(x, y, xbeg, xend, ymid+1, yend, k-(ymid-ybeg+1));
    }
  }
}
int main()//已知两个已经排好序(非减序)的序列X和Y,其中X的长度为m,Y长度为n,
//现在请你用分治算法,找出X和Y的第k小的数,算法时间复杂度为O(max{logm, logn})
//吐槽: 直接把两个数组合并一下,再用数组自带的sort排个序(快速排序O(nlogn)),再找第K小,岂不美滋滋,O(m+n)
{
   int m, n, k;
   cin >> m >> n >> k;
   int x[m];
   int y[n];
   for(int i=0; i<m; i++){
     cin >> x[i];
   }
   for(int j=0; j<n; j++){
     cin >> y[j];
   }

   cout << find(x, y, 0, m-1, 0, n-1, k) << endl;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值