<think>我们设计一个算法来求解两个等长有序序列的中位数。根据引用内容,我们可以采用分治法(二分查找)的思路。该算法的时间复杂度为O(logn),空间复杂度为O(1)。###算法设计思路设两个升序序列为A和B,长度均为n。我们需要找到这两个序列所有元素组成的升序序列的中位数。1.**二分法的思想**:每次比较两个序列的中位数,然后根据比较结果缩减搜索范围。-设A序列的当前搜索范围为A[s1,t1],中位数位置为m1=(s1+t1)/2。-设B序列的当前搜索范围为B[s2,t2],中位数位置为m2=(s2+t2)/2。-比较A[m1]和B[m2]:-如果A[m1]==B[m2],则中位数即为A[m1](或B[m2]),因为合并序列中这两个数前后会相等或接近。-如果A[m1]< B[m2],则舍弃A序列中较小的一半(即A[s1...m1])和B序列中较大的一半(即B[m2...t2])。但要注意舍弃的长度必须相等,这样才能保证剩余两个序列等长,且整体中位数仍在剩余部分中。-如果A[m1]>B[m2],则舍弃A序列中较大的一半(即A[m1...t1])和B序列中较小的一半(即B[s2...m2]),同样要求舍弃长度相等。2.**如何舍弃**?-对于A序列:在A[m1]< B[m2]时,我们需要保留A中较大的部分(即A[m1+1...t1])和B中较小的部分(即B[s2...m2])。但注意,要保证两个序列舍弃的元素数量相同。由于两个序列都是等长的,我们可以这样操作:-舍弃A的前半部分(包括中位数)?不,实际上我们可能只需要舍弃到中位数的位置,但要考虑序列长度奇偶性。-具体操作:-当序列长度为奇数时,中位数位置为中间那个元素,此时舍弃前半部分(包括中位数)和后半部分(包括中位数)都会使得舍弃长度不同。-参考引用中的做法,定义两个函数:-`prepart(int&s,int&t)`:将区间[s,t]调整为前半部分,即[s,m](m为中位位置)。-`postpart(int&s,int&t)`:将区间调整为后半部分,如果元素个数为偶数,则舍弃前半部分(包括下中位数)后,剩余上中位数开始的后半段。然而,为了简化,我们可以这样考虑:每次舍弃的长度为当前区间长度的一半(向下取整),因为两个序列同时舍弃相同数量的元素,则剩余两个序列的长度仍然相等,而且我们舍弃的元素中一定不包含中位数(因为中位数在保留的部分中)。但是,引用[3]中提到,我们要求舍弃的长度相等。另外,当n为偶数时,我们只考虑一个中位数位置(取(s+t)/2为下中位),所以我们每次舍弃掉一半(向下取整)后,剩余的两个序列长度仍然相等,并且整体中位数在剩余部分中。3.**终止条件**:当两个序列都只剩下一个元素时,取较小的那个(或者直接返回,因为最终合并后中位数在两个数之间?实际上,在等长序列中,中位数应该是这两个数的平均值?但题目要求整数,而且引用中的例子直接返回较小的?)但实际上,根据引用中的例子,当循环到两个序列只剩一个元素时,返回min(a[s1],b[s2])。但要注意,两个等长序列的中位数应该是第n小的元素(即第n-1小的元素,因为从0开始计数)?但这里每次舍弃一半后,剩下的两个元素合并后实际上中间位置是这两个元素的平均值?但题目中给的例子是11(序列S1和S2的中位数是11)。然而,当我们有两个元素时,两个序列共两个元素,合并后两个元素,取哪个?实际上,两个序列的总长度为2n,中位数应该是第n和n+1位置元素的平均值(或者题目规定为第n个位置)?题目中定义:两个序列的中位数是它们所有元素的升序序列的中位数。对于升序序列,如果总长度为2n,那么中位数是第n个位置?注意题目中例子:S1={11,13,15,17,19}(n=5),S2={2,4,6,8,20},合并后为{2,4,6,8,11,13,15,17,19,20},总长度10,中位数应该是第5个(11)和第6个(13)的平均?但题目说中位数是11。注意题目定义:序列的中位数是处在第n/2个位置的数(当n为奇数时,第(n+1)/2个,从1开始数),即第(n-1)/2(下标从0)?但实际上题目说S1的中位数是第n/2个位置的数(n=5,第2.5个?实际取第3个?)不对,题目明确说“处在第n/2个位置的数”,这里n=5,则第5/2=2(下取整)?下标为2的元素(0开始)是15。这里题目说中位数是15。那么两个序列合并后共有10个元素,按定义应该是第5个(因为10/2=5,第5个位置,下标为4?因为下标从1开始算的话就是第5个)。合并序列:1:2,2:4,3:6,4:8,5:11,所以第5个位置是11。因此,两个序列的中位数是11。注意,题目定义:两个序列的中位数是它们所有元素的升序序列的中位数。因此,总元素数为2n,那么中位数是第n个(下标从1开始)。那么我们这个算法最终需要找到第n小的数。4.**算法步骤**(循环版本,参考引用[1]代码):-初始化:s1=0,t1=n-1;s2=0,t2=n-1;-循环条件:当s1不等于t1或s2不等于t2时(即两个序列都还未缩减到只有一个元素?)-计算两个序列的中位数位置:m1=(s1+t1)/2,m2=(s2+t2)/2;-比较A[m1]和B[m2]:-相等:直接返回A[m1](因为合并后A[m1]就是第n小的数?需要证明)但实际上,相等时并不一定就是第n小的数,因为前面可能有多个比它小的数。但是,根据分治策略,当相等时,我们可以证明它就是我们要的中位数。为什么?因为合并序列中,有足够多的元素在它前面。具体来说:-在A序列中,有m1(下标)个元素小于等于A[m1](包括A[m1]自己,所以小于A[m1]的元素个数是m1)。-在B序列中,有m2个元素小于等于B[m2](即小于等于A[m1])?所以总共小于等于A[m1]的元素个数至少为(m1+1)+(m2+1)=m1+m2+2。但我们需要第n小的数(总元素2n,第n小的数就是第n个位置)。注意:初始时,两个序列各n个元素,合并后第n小的数需要满足:小于等于它的元素至少有n个。那么当A[m1]=B[m2]时,假设这个数等于x,则在A序列中,有(m1+1)个元素<=x(下标0到m1),在B序列中,有(m2+1)个元素<=x。又因为m1和m2都是中位数的下标,初始时,m1=m2=(n-1)/2(下标从0开始,所以有m1+1=(n+1)/2个元素)。所以总共有:(n+1)/2+(n+1)/2=(n+1)个元素<=x。而n=(n+1)/2+(n-1)/2?这里需要更精确的计算。实际上,初始时,如果n是奇数,m1=m2=(n-1)/2,所以小于等于x的元素个数是(n+1)/2+(n+1)/2=n+1,大于n。所以x不可能是第n小的数?矛盾。因此,我们需要重新审视。实际上,引用中的算法是可行的,它的思路是不断去掉两个序列中不可能包含中位数的部分,直到每个序列只剩下一个元素,然后取较小者。但我们看到,它的终止条件是当s1等于t1且s2等于t2(或者用或,只要有一个不等就继续?)实际上,循环条件中的`||`表示只要两个序列有一个还没有收缩到只有一个元素(长度为1),就继续循环。那么每次我们都在舍弃相同数量的元素(两个序列各舍弃一半(向下取整)),所以两个序列的长度始终保持相同,直到最后变为1。而最终,我们返回a[s1]和b[s2]中的最小值。但是为什么最小值就是中位数?因为合并后有两个元素(两个序列各自剩余的最小值),那么这两个元素中小的那个应该是总序列中第n小的元素吗?注意,在每次舍弃过程中,我们保留了两个序列中部分元素,并且舍弃的元素都比保留的部分小(或者大),但舍弃部分一定不包含中位数。证明:我们每次舍弃了两个序列中一个序列的前半部分和另一个序列的后半部分,而且舍弃的元素个数相同。因此,总的中位数不会发生变化(因为舍弃的元素个数相同,所以中位数位置不变?)。更严格地说,每次舍弃后,我们寻找的是剩余两个序列合并后的中位数(即整体中位数在剩余部分中的位置)。因为每次舍弃的较小部分和较大部分的数量相等(且都小于等于总长度的一半),所以舍弃后整体中位数仍然在剩余部分中。具体步骤:1.初始总长度为2n,中位数是第n小的元素(从小到大)。2.经过一次迭代,我们舍弃了两个序列中各大约一半的元素(向下取整)。假设当前每个序列长度为L,则我们舍弃了L//2个元素(A序列的前半部分或者后半部分,B序列的另一半),总共舍弃了L个元素(因为两个序列各舍弃L//2,所以一共L)。但注意,舍弃后剩余的两个序列仍然等长(L-L//2=(L+1)//2)。那么剩下的元素总数是2*((L+1)//2)=L+1(当L为奇数)或L(当L为偶数)。因此,整体中位数在剩余部分的位置:舍弃了L个元素,那么原来第n小的数现在在剩余部分中是第(n-舍弃元素数量)小。而舍弃元素数量是L,所以剩下部分的中位数应该是第n-L小。但这里我们保持了两个序列等长,所以算法继续对两个新序列进行操作。最终,当每个序列只剩一个元素时(总长度2),那么第n小的数应该是这两个元素中较小的一个(因为在此之前,我们舍弃了n-1个元素,那么剩下的两个元素中第一个就是第n个)。但实际上n在迭代过程中已经改变?不对,我们每次舍弃的元素数量累加起来应该正好等于n-1(最后剩两个元素,第n小就是这两个元素的最小值?)但是,我们需要的是第n小的元素,而最后两个序列剩下两个元素,那么合并后就是两个元素,它们在整个序列中的位置分别是第n和n+1?不对,因为我们每次舍弃后,剩余部分的中位数就是整体中位数(也就是第n小的数)仍然在剩余部分中,而最后剩余的两个序列只剩下一个元素,所以两个元素合并后,最小的那个就是剩下的所有元素中最小的,也就是整个序列的第n小的数。为什么?因为我们在舍弃过程中保证了:整个序列的前n-1个最小的元素都被舍弃了?所以剩下的两个元素中,较小的就是第n个。因此,算法返回a[s1]和b[s2]的最小值。###代码实现根据引用[1]的C代码,我们可以写一个Python版本:```pythondefprepart(s,t):#将区间[s,t]调整为前半部分,即[s,m]m=(s+t)//2returns,m#新的区间起点不变,终点变为mdefpostpart(s,t):#将区间调整为后半部分m=(s+t)//2if(s+t)%2==0:#当前区间元素个数为奇数,舍弃部分包括中位数,所以保留区间为[m,t]returnm,telse:#元素个数为偶数,舍弃前半部分(包括下中位),保留后半部分(从m+1开始)returnm+1,tdefmedian_of_two_sorted_arrays(A,B):n=len(A)#初始化两个序列的区间s1,t1=0,n-1s2,t2=0,n-1whiles1!=t1ors2!=t2:#两个序列都未收缩到只有一个元素#计算两个序列的中位数位置m1=(s1+t1)//2m2=(s2+t2)//2ifA[m1]==B[m2]:returnA[m1]elifA[m1]< B[m2]:#舍弃A的前半部分和B的后半部分#注意:这里使用prepart和postpart来调整区间s1,t1=postpart(s1,t1)#A保留后半部分s2,t2=prepart(s2,t2)#B保留前半部分else:s1,t1=prepart(s1,t1)#A保留前半部分s2,t2=postpart(s2,t2)#B保留后半部分#此时两个序列都只剩一个元素(当前区间[s1,t1]和[s2,t2]均为单个元素)returnmin(A[s1],B[s2])```但是,上述函数`prepart`和`postpart`返回的是新的区间[s,t]的起点和终点,我们重新赋值给s1,t1和s2,t2。这样,每次循环都将区间缩小到一半。但是我们需要保证每次舍弃的长度相同吗?是的,这样两个序列的剩余长度才相等。注意:函数`prepart(s,t)`返回的区间长度是:原来长度的一半(向上取整,如果原始长度为L,则前半部分长度为(L+1)//2)。而`postpart`返回的区间长度:当原始长度为奇数时,返回的长度为(L+1)//2(后半部分包括中位数),当为偶数时,返回的长度为L//2(后半部分)。那么每次两个序列各舍弃的元素个数:对于A[m1]<B[m2]的情况,A序列被舍弃的部分是从s1到m1-1(如果保留的是后半部分,那么舍弃了前半部分),实际舍弃的元素个数取决于原始长度。但是,由于两个序列长度相同,而且我们同时进行一样的操作(一个序列保留前半部分,一个序列保留后半部分),但是保留的前半部分和后半部分的长度不一定相等?因此,我们需要保证:两个序列舍弃的元素个数相同。在上述实现中,`prepart`保留前半部分,前半部分的长度:设原始区间[s,t]长度为L=t-s+1,则前半部分的长度为(L+1)//2(取中点m后,包含起点s到m,共(m-s+1)=(L+1)//2)。而`postpart`保留后半部分:当L为奇数时,长度为(L+1)//2(因为保留m到t,共(t-m+1)=(L+1)//2);当L为偶数时,长度为L//2(保留m+1到t,共(t-m)=L/2)。那么,这两个函数保留的长度不一定相等。为了确保每次舍弃的长度相等,我们必须调整操作,使得舍弃的部分长度相同。引用[1]的C代码中,两个函数分别如下:```cvoidprepart(int&s,int&t)//求a[s..t]序列的前半个子序列{intm=(s+t)/2;t=m;}voidpostpart(int&s,int&t){intm=(s+t)/2;if((s+t)%2==0)//序列中有奇数个元素s=m;else//序列中有偶数个元素s=m+1;}```这样:-`prepart`:将区间调整为[s,m],即前半部分。新长度=(m-s+1)=((s+t)/2-s+1)=(t-s+2)/2?需要看奇偶性。实际上,设原始区间长度L=t-s+1。中位数位置m=(s+t)//2(整数除法,向下取整)。新区间为[s,m],长度为m-s+1。因为m=(s+t)//2,所以:-若L为奇数:设L=2k+1,则m=s+k,长度为s+k-s+1=k+1=(L+1)//2。-若L为偶数:设L=2k,则m=s+k-1(因为(s+t)/2向下取整,s+t=2s+2k-1,除以2为s+k-0.5,向下取整为s+k-1),长度为s+k-1-s+1=k=L/2。-`postpart`:-若(s+t)是偶数(即L为奇数):(s+t)%2==0->true,则s=m,新区间为[m,t]。长度=t-m+1=t-(s+t)/2+1。因为L=2k+1,m=(s+t)/2=s+k,所以t-m+1=(t-s-k)+1=(2k-k)+1=k+1=(L+1)//2。-若L为偶数:(s+t)为奇数(s+t为奇数),则s=m+1,新区间为[m+1,t]。长度=t-m-1+1=t-m=(t-s)/2=(L-1)/2?不对,L=2k,m=(s+t)/2=(s+t)//2(整数除法)=s+k-1(因为s+t=2s+2k-1,除以2为s+k-0.5,向下取整为s+k-1)。所以长度=t-(m+1)+1=t-m=(s+t)-(s+k-1)...直接计算:t-m=t-(s+t)/2。而t=s+2k-1(因为L=2k,区间[s,s+2k-1]),则m=(s+s+2k-1)/2=s+k-0.5->s+k-1(向下取整),所以t-m=(s+2k-1)-(s+k-1)=k。所以无论哪种情况,`prepart`返回的区间长度和`postpart`返回的区间长度并不总是相等。在L为奇数时,`prepart`返回的长度=(L+1)//2,`postpart`返回的长度也是(L+1)//2。在L为偶数时,`prepart`返回的长度=L/2(即k),`postpart`返回的长度是k(即L/2)。因此,相同。所以,每次操作后,两个序列的新区间长度相等。根据引用[1]的代码,每次舍弃后,两个序列的新区间长度相等,并且为原来区间长度的一半(向上取整)。###测试用题目中的例子:A=[11,13,15,17,19],B=[2,4,6,8,20]算法过程:初始:s1=0,t1=4;s2=0,t2=4.第一次迭代:m1=(0+4)//2=2->A[2]=15m2=(0+4)//2=2->B[2]=6因为15>6,所以对A取前半部分(prepart),即s1=0,t1=(0+4)//2=2->[0,2](即A[0..2]:11,13,15)对B取后半部分(postpart):判断(s2+t2)%2=(0+4)%2=0(偶数?注意:0+4=4,4%2=0,但区间长度5是奇数。实际上,代码中(s+t)%2==0表示区间长度(t-s+1)为奇数?因为区间长度为奇数时,s+t=2m,是偶数;而长度为偶数时,s+t=2m+1,奇数。所以:这里s2+t2=0+4=4,4%2=0,所以为奇数长度,则s2更新为m2=2,即新区间为[2,4](B[2..4]:6,8,20)所以两个序列的区间变为:A:[11,13,15]->长度3B:[6,8,20]->长度3第二次迭代:A:s1=0,t1=2->m1=(0+2)//2=1->A[1]=13B:s2=2,t2=4->m2=(2+4)//2=3->B[3]=813>8,所以对A取前半部分:prepart:s1=0,t1=(0+2)//2=1->[0,1](A[0..1]:11,13)对B取后半部分:postpart:s2+t2=2+4=6,6%2=0->为奇数长度,所以s2更新为m2=3->[3,4](B[3..4]:8,20)两个序列的区间:A:[11,13]->长度2B:[8,20]->长度2第三次迭代:A:s1=0,t1=1->m1=(0+1)//2=0->A[0]=11B:s2=3,t2=4->m2=(3+4)//2=3->B[3]=811>8,所以对A取前半部分:prepart:[0,0](因为t1=(0+1)//2=0)->[0,0](A[0]:11)对B取后半部分:postpart:s2+t2=3+4=7->奇数(7%2=1)?不对,实际上代码判断条件是(s+t)%2==0,7%2=1不等于0,所以是偶数长度(区间长度2为偶数),因此s2=3+1=4->新区间为[4,4](B[4]:20)现在两个序列的区间:A[0]和B[4],且不满足s1!=t1?s1=0,t1=0->相等;s2=4,t2=4->相等,结束循环。返回min(A[0],B[4])=min(11,20)=11。结果正确。###优化另一种常见的解法是直接利用二分查找在较短的序列上查找。但是本算法中两个序列等长,因此时间复杂度为O(logn)。空间复杂度O(1)。###代码实现(另一种更简洁的写法,直接每次迭代缩小区间)以上面的分析为基础,我们可以直接操作区间下标,避免定义辅助函数:```pythondefmedian_of_two_sorted_arrays(A,B):n=len(A)s1,t1=0,n-1s2,t2=0,n-1whiles1< t1ands2< t2:#同时两个序列长度大于1m1=(s1+t1)//2m2=(s2+t2)//2#如果两个序列的中位数相等,直接返回ifA[m1]==B[m2]:returnA[m1]#根据比较决定舍弃哪一部分ifA[m1]< B[m2]:#舍弃A的前半部分和B的后半部分#判断当前区间的元素个数奇偶性(以确定舍弃部分)#元素个数:len1=t1-s1+1,len2=t2-s2+1#我们要求舍弃的长度相同,即k=min(m1-s1+1,t2-m2+1)???不对,我们要求每次舍弃的长度相同,直接取舍弃一半(向下取整)即可,因为两个序列长度相等,所以每次迭代舍弃的数量也相等(各舍弃当前长度的一半(向下取整))。#但更简单的方法:按照引用的方法,更新区间即可。#对A:舍弃前半部分(包括中位数)->即保留后半部分#对B:舍弃后半部分(包括中位数)->即保留前半部分#计算新的区间#A保留后半部分:从m1到t1(注意:当长度为奇数时,保留的后半部分包括中位数;当为偶数时,保留的后半部分不包括中位数?)#但用引用的方法:A做postpart,B做preparts1=m1#这样不行,需要根据奇偶性#我们重新写逻辑:根据区间长度奇偶性更新区间#不过,为了避免混淆,我们可以直接使用两个函数#由于我们之前已经分析了引用代码,这里使用函数实现#或者,我们按照引用代码的逻辑,用函数实现区间更新。为了避免重新定义函数,我们直接内联逻辑:whiles1!=t1ors2!=t2:m1=(s1+t1)//2m2=(s2+t2)//2ifA[m1]==B[m2]:returnA[m1]elifA[m1]< B[m2]:#更新区间:A保留后半段,B保留前半段#计算下一个区间:A[s1..t1]的后半部分if(s1+t1)%2==0:#当前奇数个元素:A保留后半部分包括中位数[m1,t1]s1=m1else:#偶数个:A保留后半部分(从m1+1开始)s1=m1+1#B保留前半部分:[s2,m2]t2=m2else:#A[m1]>B[m2]:A保留前半段,B保留后半段t1=m1#A保留前半段[s1,m1]#B保留后半段:需要判断奇偶if(s2+t2)%2==0:s2=m2else:s2=m2+1returnmin(A[s1],B[s2])```但上面的代码中,我们内联了`prepart`和`postpart`。注意,在A[m1]<B[m2]时,A更新为后半部分,所以我们用`postpart`的操作(更新s1),而B更新为前半部分,用`prepart`(更新t2)。同理,在A[m1]>B[m2]时,A更新为前半部分(用`prepart`,即更新t1),B更新为后半部分(用`postpart`,即更新s2)。###测试例子使用上面的例子测试内联代码:初始:A:[11,13,15,17,19],B:[2,4,6,8,20]->s1=0,t1=4,s2=0,t2=4第一次迭代:A[m1=2]=15,B[m2=2]=6->15>6->进入else分支t1=m1=2#所以A区间为[0,2]s2+t2=0+4=4,4%2==0->True,所以s2=m2=2#B区间为[2,4]然后迭代:A:[11,13,15]和B:[6,8,20]m1=(0+2)//2=1->A[1]=13;m2=(2+4)//2=3->B[3]=8;13>8->进入else分支t1=m1=1->A区间[0,1]s2+t2=2+4=6,6%2==0->True->s2=m2=3->B区间[3,4]第三次迭代:s1=0,t1=1;s2=3,t2=4m1=(0+1)//2=0->A[0]=11;m2=(3+4)//2=3->B[3]=8->11>8->else分支t1=0->A区间[0,0]s2+t2=3+4=7,7%2=1,不是0->False,所以s2=3+1=4->B区间[4,4]然后循环条件判断:s1==t1==0,s2==t2==4,满足s1==t1且s2==t2?循环条件为s1!=t1ors2!=t2:现在s1==t1且s2==t2,所以不进入循环。返回min(A[0],B[4])=min(11,20)=11。正确。###注意事项1.该算法要求两个序列等长。2.如果两个序列长度不等,则需要更复杂的算法(比如转为寻找第k小的数)。###完整代码(内联逻辑)这里我们给出完整的Python实现:```pythondefmedian_of_two_sorted_arrays(A,B):n=len(A)s1,t1=0,n-1s2,t2=0,n-1whiles1!=t1ors2!=t2:m1=(s1+t1)//2m2=(s2+t2)//2ifA[m1]==B[m2]:returnA[m1]elifA[m1]< B[m2]:#更新A:保留后半段->相当于A做postpartif(s1+t1)%2==0:#长度奇数s1=m1else:s1=m1+1#更新B:保留前半段->相当于B做prepartt2=m2#无论奇偶,都是[t2=m2]?因为prepart就是让t2=m2else:#更新A:保留前半段->相当于A做prepartt1=m1#更新B:保留后半段->相当于B做postpartif(s2+t2)%2==0:s2=m2else:s2=m2+1returnmin(A[s1],B[s2])```测试例子:A=[1,3,5,7,9],B=[2,4,6,8,10]正确答案:合并后为[1,2,3,4,5,6,7,8,9,10],中位数为第5个元素5(下标从1开始)。运行算法:初始:s1=0,t1=4;s2=0,t2=4m1=2->A[2]=5;m2=2->B[2]=6->5<6->进入A[m1]<B[m2]分支A的区间:(0+4)%2=0?0+4=4,4%2==0->True,sos1=m1=2->A变为[5,7,9]B的区间:t2=m2=2->B变为[2,4,6]下一轮:s1=2,t1=4;s2=0,t2=2m1=(2+4)//2=3->A[3]=7;m2=(0+2)//2=1->B[1]=4->7>4->进入elset1=3(保留A的前半部分:即[2,3]->但是s1=2,t1=3表示[A[2],A[3]]=[5,7]?不对,应该是t1=3表示区间[2,3](两个元素)?我们需要重新检查)实际上,在else分支中,我们更新了t1=m1,即t1=3(原来t1=4),所以A的新区间为[s1,t1]=[2,3],即元素[5,7]。B:s2+t2=0+2=2,2%2==0->True,所以s2=m2=1->B的新区间为[1,2]->[4,6]下一轮:s1=2,t1=3;s2=1,t2=2m1=(2+3)//2=2->A[2]=5m2=(1+2)//2=1->B[1]=4->5>4->elset1=m1=2->A的新区间[2,2](5)B:s2+t2=1+2=3,3%2=1->偶数长度,所以s2=m2+1=1+1=2->B[2,2](6)然后返回min(5,6)=5->正确。A=[1,2,3],B=[4,5,6]->中位数为3?不对。合并后[1,2,3,4,5,6],中位数应该是第3和第4个元素的平均值?但题目要求两个序列的中位数是合并后升序序列的中位数。根据题目定义,两个序列的中位数是第n小的元素(总元素2n,中位数是第n个)。对于n=3,中位数是第3小的元素(从小到大第3个)。合并后:第1:1,第2:2,第3:3,所以是3。那么A=[1,2,3],B=[4,5,6],算法:m1=(0+2)//2=1->A[1]=2;m2=(0+2)//2=1->B[1]=5->2<5->A:更新:s1+t1=0+2=2为偶数->s1=m1=1->A[1,2](2,3)B:t2=m2=1->B[0,1](4,5)下一轮:s1=1,t1=2;s2=0,t2=1m1=(1+2)//2=1->A[1]=2;m2=(0+1)//2=0->B[0]=4->2<4->A:s1+t1=1+2=3奇数?3%2=1->偶数?我们代码中判断%2==0?3%2!=0->所以s1=m1+1=2->A[2,2](3)B:t2=0->B[0,0](4)返回min(3,4)=3->正确。###如果两个序列长度不同怎么办?该算法要求两个序列等长。如果不等长,需要另一种算法(如二分查找第k小的数)。###相关问题§§相关问题§§1.如何证明该算法的正确性?2.如果两个有序序列长度不同,如何求它们的中位数?3.这个算法的时间复杂度和空间复杂度是多少?</think>
### 算法设计:求解两个有序序列的中位数
给定两个等长的升序序列$A$和$B$,长度均为$n$,设计一个高效算法求解它们合并后的升序序列的中位数(即第$n$小的元素)。以下是基于分治思想的二分查找算法,时间复杂度为$O(\log n)$,空间复杂度为$O(1)$。
#### 算法原理
1. **基本思想**:
- 分别计算两个序列的当前中位数$A[m_1]$和$B[m_2]$($m_1 = \lfloor(s_1+t_1)/2\rfloor$, $m_2 = \lfloor(s_2+t_2)/2\rfloor$)。
- 比较$A[m_1]$和$B[m_2]$:
- 若相等,则为中位数;
- 若$A[m_1] < B[m_2]$,则舍弃$A$的前半部分和$B$的后半部分;
- 若$A[m_1] > B[m_2]$,则舍弃$A$的后半部分和$B$的前半部分。
- 重复以上过程,直到两个序列均缩减为单个元素,返回较小值。
2. **关键操作**:
- **`prepart`**:保留序列的前半部分($[s, m]$)。
- **`postpart`**:保留序列的后半部分(长度奇偶性决定保留$[m, t]$或$[m+1, t]$)。
- 每次舍弃长度相等,确保剩余序列等长且中位数不变。
#### Python代码实现
```python
def median_of_two_sorted_arrays(A, B):
n = len(A)
s1, t1 = 0, n - 1
s2, t2 = 0, n - 1
while s1 != t1 or s2 != t2:
m1 = (s1 + t1) // 2
m2 = (s2 + t2) // 2
if A[m1] == B[m2]:
return A[m1]
elif A[m1] < B[m2]:
# 舍弃A的前半部分和B的后半部分
if (s1 + t1) % 2 == 0: # A长度奇数
s1 = m1
else: # A长度偶数
s1 = m1 + 1
t2 = m2 # B保留前半部分
else:
# 舍弃A的后半部分和B的前半部分
t1 = m1 # A保留前半部分
if (s2 + t2) % 2 == 0: # B长度奇数
s2 = m2
else: # B长度偶数
s2 = m2 + 1
return min(A[s1], B[s2])
# 测试示例
A = [11, 13, 15, 17, 19]
B = [2, 4, 6, 8, 20]
print("中位数:", median_of_two_sorted_arrays(A, B)) # 输出: 11
```
#### 算法分析
| 步骤 | 操作 | 时间复杂度 |
|------------|-------------------------------|-----------|
| 初始化区间 | 设置$s_1,t_1$和$s_2,t_2$ | $O(1)$ |
| 循环迭代 | 每次区间长度减半 | $O(\log n)$ |
| 比较中位数 | 比较$A[m_1]$和$B[m_2]$ | $O(1)$ |
| 缩减区间 | 调用`prepart`/`postpart`逻辑 | $O(1)$ |
| 返回结果 | 返回$\min(A[s_1], B[s_2])$ | $O(1)$ |
#### 示例说明
- **输入**:$A = [11,13,15,17,19]$, $B = [2,4,6,8,20]$
- **合并序列**:$[2,4,6,8,11,\underline{13},15,17,19,20]$
- **中位数**:第$5$小的元素($n=5$)为$\underline{11}$,与算法输出一致[^1][^2][^3]。
---
### 相关问题