两个长度相同有序序列的中位数

我当时想的第一个简略算法:把两个序列合并后打印第n个元素,不出所料超时了

第二个思路,存储两个序列,然后轮流从两个序列里查找当前最小元素,找到第n个最小元素打印即可,这是个有序序列所以很好找

 

然后就是书上的二分法思路:

分别取l1,l2的中位数a,b,则并集序列的中位数在a,b之间。

这是显然的,假设a<b,若中位数小于a,在l1中有一半的元素大于a,在l2中有多于一半的元素大于a,则有多于一般的数大于"中位数",矛盾。

此时去掉l1左边的k个数,再去掉l2右边的k个数,中位数的位置不变,这玩意也好理解,在合并序列的两侧分别去除相同数目的元素,中位数位置不变

然后两个序列就这样不断二分求中位数,直到两个中位数相同,此即并集中位数,或两个取到边界,则较小的是中位数

 

当两个序列长度不相同时,我脑子不好用没想出来,在这里找到了能看懂的办法,而且用在长度相同序列好像还更简单

详细通俗的思路分析,多解法

(解法4)

 我自己的理解如下

设数组为长度为m的arr1和长度为n的arr2,将arr1分为左右两部分,左边有i个元素,右边有m-i个元素,同理将arr2分为j,n-j的两部分

显然0<=i<=m,0<=j<=n;

若n+m为偶数,当左边元素数i+j等于右边元素数n+m-i-j,且max(arr1[i-1],arr2[j-1])<min(arr2[i],arr2[j])时,两个和序列的中位数就为左边最大数和右边最小数的平均值

若n+m为奇数,不妨设中位数找到时左边数比右边数多一个,则当i+j=n+m+1-i-j,且max(arr1[i-1],arr2[j-1])<min(arr2[i],arr2[j])时,和序列的中位数为左边最大数

 

因为m,n都是整数,所以(m+n+1)/2与(m+n)/2相同,则j=(m+n+1)/2-i,此时只需要找到i的合适位置即可,用二分法进行查找

因为0<=i<=m,为使j>0,就需要m>=n;

 

c代码如下

#include<stdio.h>

int max(int a,int b){return a>b?a:b;}
int min(int a,int b){return a<b?a:b;}

int main(){
    int n,m,i,j,imax,imin,maxleft=0,minright=0,z;
    int arr1[100000],arr2[100000];
    scanf("%d %d",&m,&n);    
  //我这只适用m>=n的输入数据,实际使用时还需要修改
    for(z=0;z<=m-1;z++) scanf("%d",&arr1[z]);
    for(z=0;z<=n-1;z++) scanf("%d",&arr2[z]);

    imax=m,imin=0;
    while(imin<=imax){
     //以下四行是对i进行二分查找 i
=(imax+imin)/2; j=(m+n+1)/2-i; if(i>0&&j<n&&arr1[i-1]>arr2[j]) imax=i-1; else if(j>0&&i<m&&arr2[j-1]>arr1[i]) imin=i+1;
    
else{ if(i==0) maxleft=arr2[j-1];      //arr1中所有的数都大于中位数,左边没有数了 else if(j==0) maxleft=arr1[i-1];   //arr2中所有的数都大于中位数,左边没有数了 else maxleft=max(arr1[i-1],arr2[j-1]); if((m+n)%2==1){             //和为奇数,直接输出左边最大值就行 printf("%d",maxleft); return 0; }
if(i==m) minright=arr2[j];      //arr1中所有的数都小于中位数,右边没有数了 else if(j==n) minright=arr1[i];   //arr2中所有的数都小于中位数,右边没有数了 else minright=min(arr1[i],arr2[j]); printf("%d",(minright+maxleft)/2); return 0; } } }

 

posted @ 2021-10-15 23:18  朗风lwind  阅读(87)  评论(0)    收藏  举报