请教下c语言分治算法的一道题目,两个有序数序列中找第k小

yolina_379653734 2013-12-06 11:12:41
/**
17082 两个有序数序列中找第k小

Description
已知两个已经排好序(非减序)的序列X和Y,其中X的长度为m,Y长度为n,
现在请你用分治算法,找出X和Y的第k小的数,算法时间复杂度为O(max{logm, logn})。

此题请勿采用将序列X和Y合并找第k小的O(m+n)的一般方法,要充分利用X和Y已经排好序的这一特性。

输入格式
第一行有三个数,分别是长度m、长度n和k,中间空格相连(1<=m,n<=100000; 1<=k<=m+n)。
第二行m个数分别是非减序的序列X。第三行n个数分别是非减序的序列Y。

输出格式
序列X和Y的第k小的数。

输入样例
5 6 7
1 8 12 12 21
4 12 20 22 26 31

输出样例
20

提示

假设:X序列为X[xBeg...xEnd],而Y序列为Y[yBeg...yEnd]。

将序列X和Y都均分2段,即取X序列中间位置为xMid (xMid = xBeg+(xEnd-xBeg)/2),也同理取序列Y中间位置为yMid。
比较X[xMid]和Y[yMid]的大小,此时记录X左段和Y左段元素个数合计为halfLen,即halfLen = xMid-xBeg+yMid-yBeg+2。

1. 当X[xMid] < Y[yMid]时,在合并的数组中,原X[xBeg...xMid]所有元素一定在Y[yMid]的左侧,
(1) 若k < halfLen,则此时第k大的元素一定不会大于Y[yMid]这个元素,故以后没有必要搜索 Y[yMid...yEnd]这些元素,可弃Y后半段数据。
此时只需递归的对X序列+Y序列的前半段,去搜索第k小的数。

(2) 若k >= halfLen,则此时第k大的元素一定不会小于X[xMid]这个元素,故以后没有必要搜索 X[xBeg...xMid]这些元素,可弃X前半段数据。
此时只需递归的对X序列的后半段+Y序列,去搜索第 k-(xMid-xBeg+1)小的数。

2. 当X[xMid] >= Y[yMid]时,在合并的数组中,原Y[yBeg...yMid]的所有元素一定在X[xMid]的左侧,
(1) 若k < halfLen,则此时第k大的元素一定不会大于X[xMid]这个元素,故以后没有必要搜索 X[xMid...xEnd]这些元素,可弃X后半段数据。
此时只需递归的对X序列的前半段+Y序列,去搜索第k小的数。

(2) 若k >= halfLen,则此时第k大的元素一定不会小于Y[yMid]这个元素,故以后没有必要搜索 Y[yBeg...yMid]这些元素,可弃Y前半段数据。
此时只需递归的对X序列+Y序列的后半段,去搜索第 k-(yMid-yBeg+1)小的数。

至于递归的边界自己思考一下,如何来写。

效率分析:

T(m,n)表示对长度为m的有序的X序列和长度为n的有序的Y序列,搜索第k小元素的复杂度。
T(m,n)=1 m=0或n=0
T(m,n) <= max{T(m/2,n), T(m,n/2)} + O(1)

则T(m,n) = O(max{logm, logn})
*/
#include<stdio.h>
#include<string.h>
#include<iostream>
using namespace std;

int BinarySearch(int X[],int Y[],int k,int m,int n)
{
int C[50001];//作为临时数组
int xBeg=0,xEnd=m-1;
int yBeg=0,yEnd=n-1;
int xMid,yMid;
int halfLen;
int i,j,flag=0;
xMid=(xEnd-xBeg)/2+xBeg;
yMid=(yEnd-yBeg)/2+yBeg;

halfLen=xMid-xBeg+yMid-yBeg+2;
if(k==1||m==1||n==1)
{
if(k==1)
if(X[0]>Y[0])
return Y[0];
else
return X[0];
if(m==1)
for(i=0,j=0; j<n+1; j++)
if(Y[i]>=X[0]&&flag==0)
{
C[j]=X[0];
flag=1;
}
else
C[j]=Y[i++];
if(n==1)
for(i=0,j=0; j<m+1; j++)
if(X[i]>=Y[0]&&flag==0)
{
C[j]=Y[0];
flag=1;
}
else
C[j]=X[i++];

return C[k-1];
}
else
{
//分析X[xMid]<Y[yMid]的情况
if(X[xMid]<Y[yMid])
{
//对比k与halfLen的大小
if(k<halfLen)
{
//Y[yMid]之后的序列都剔除
for(i=0; i<=yMid; i++)
C[i]=Y[i];
return BinarySearch(X,C,k,m,yMid-yBeg+1);
}
if(k>=halfLen)
{
//X[xMid]之前的序列都剔除
for(i=xMid+1,j=0; i<=xEnd; i++,j++)
C[j]=X[i];
k=k-(xMid-xBeg+1);
return BinarySearch(C,Y,k,xEnd-xMid,n);
}
}

if(X[xMid]>=Y[yMid])
{
//分析X[xMid]>=Y[yMid]的情况
if(k<halfLen)
{
// X[xMid]之后的序列都剔除
for(i=0; i<=xMid; i++)
C[i]=X[i];
return BinarySearch(C,Y,k,xMid-xBeg+1,n);
}
if(k>=halfLen)
{
//Y[yMid]之前的序列都剔除
for(i=yMid+1,j=0; i<=yEnd; i++,j++)
C[j]=Y[i];
k=k-(yMid-yBeg+1);
return BinarySearch(X,C,k,m,yEnd-yMid);
}
}
}

}

main()
{
int i,m,n,k;
int X[100001],Y[100001];
cin >> m>>n>>k;
for(i=0; i<m; i++)
cin>>X[i];
for(i=0; i<n; i++)
cin>>Y[i];
cout<<BinarySearch(X,Y,k,m,n)<<endl;
}




/**以上是我的实现,感觉没问题了啊,可是测试时,系统提交的检测数据前两组是没问题的:
第1组:
5 6 7
1 8 12 12 21
4 12 20 22 26 31
(输出结果是20)
第2组:
15 20 7
1 8 12 12 21 25 33 41 43 47 52 57 58 65 66
1 6 8 15 21 22 26 28 31 33 35 36 42 50 55 62 68 69 77 86
(输出结果是12)
可是第三组数据:
15 20 12
1 8 12 12 21 25 33 41 43 47 52 57 58 65 66
1 6 8 15 21 22 26 28 31 33 35 36 42 50 55 62 68 69 77 86
(输出应该是25)
程序最老运行出错,为什么呢?
请问哪里有问题了?
谢谢了啊!














...全文
457 1 打赏 收藏 转发到动态 举报
写回复
用AI写文章
1 条回复
切换为时间正序
请发表友善的回复…
发表回复
还有多远 2013-12-07
  • 打赏
  • 举报
回复
刚开始只能想起来O(logm * logn)的解法,因为在二分查找的时候,忘记了更新查找的范围,而O(logm + logn)貌似重点就是两个范围的同步更新呢。抛砖引玉,仅供参考:

#include <cstdio>
#include <algorithm>
#include <utility>
using namespace std;

int findKth(int x[], int m, int y[], int n, int K)
{
    int left = 0, right = m, mid;
    int i = 0, j = n;
    pair<int*,int*> res;
    
    while(left < right){
        mid = (left+right)>>1;
        res = equal_range(y+i, y+j, x[mid]);
        if(mid+(res.first-y)+1 <= K &&
           mid+(res.second-y)+1 >= K) return x[mid];
        else if(mid+(res.first-y)+1 < K){//must be some one bigger in X
             left = mid+1;
             i = res.second-y;
        }
        else{//must be some one smaller in X
             right = mid;
             j = res.first-y;
        }
    }
    //can not find in X, so it's in Y
    return findKth(y, n, x, m, K);
}

int main()
{
    int x[] = {1,1,5,7,8}, m = sizeof(x)/sizeof(x[0]);
    int y[] = {2,3,3,4,6,8,9,9,10,10,10}, n = sizeof(y)/sizeof(y[0]);
    
    for(int i = 1; i <= m+n; ++i){
        printf("%dth smallest is %d\n", i, findKth(x, m, y, n, i));
    }
    
    getchar();
    return 0;
}

64,637

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧