找出两个升序序列的中位数算法理解

题目描述:

  找出两个等长的升序序列的中位数,如序列 S 1 = ( 11 , 13 , 15 , 17 , 19 ) S_1=(11,13,15,17,19) S1=(11,13,15,17,19) S 2 = ( 2 , 4 , 6 , 8 , 20 ) S_2=(2,4,6,8,20) S2=(2,4,6,8,20)。则序列 S 1 S_1 S1 S 2 S_2 S2的中位数为11。(注意,我们这里定义处于有序序列第 ⌈ N / 2 ⌉ \lceil {N/2} \rceil N/2个位置的数为中位数,而不是取中间两个数的均值。)

题目来源:王道数据结构。


算法思想:

分别求序列 A A A和序列 B B B的中位数,设为 a a a b b b,求序列A、B的中位数过程如下:

  1. a = b a=b a=b,则 a a a b b b即为所求中位数,算法结束;
  2. a < b a<b a<b,则舍弃序列 A A A中较小的一半,同时舍弃序列 B B B中较大的一半,要求两次舍弃的长度相等。
  3. a > b a>b a>b,则舍弃序列 A A A中较大的一半,同时舍弃序列 B B B中较小的一半,要求两次舍弃的长度相等。

在保留的两个升序序列中,重复1、2、3,直到两个序列中均只剩一个元素时结束,较小者即为所求的中位数。


算法理解:

  首先我们通过一个简单的例子来验证一下这个算法的正确性。假设现在有两个长度为5的有序升序序列 A = ( a 1 , a 2 , a 3 , a 4 , a 5 ) A=(a_1,a_2,a_3,a_4,a_5) A=(a1,a2,a3,a4,a5) B = ( b 1 , b 2 , b 3 , b 4 , b 5 ) B=(b_1,b_2,b_3,b_4,b_5) B=(b1,b2,b3,b4,b5)。把它们合并为一个升序序列为 C = ( a 1 , b 1 , b 2 , a 2 , a 3 , b 3 , a 4 , a 5 , b 4 , b 5 ) C=(a_1,b_1,b_2,a_2,a_3,b_3,a_4,a_5,b_4,b_5) C=(a1,b1,b2,a2,a3,b3,a4,a5,b4,b5)。根据合并后的序列我们可以得到两个序列的中位数 c = a 3 c=a_3 c=a3。我们现在按照上面的算法走一遍:

  1. 找出序列 A A A B B B的中位数 a 3 a_3 a3 b 3 b_3 b3,显然此时 a 3 ≤ b 3 a_3\leq b_3 a3b3,我们这里先讨论小于的情况。
  2. 根据算法,我们需要舍弃序列 A A A中小于 a 3 a_3 a3的元素以及序列 B B B中大于 b 3 b_3 b3的元素,此时 A = ( a 3 , a 4 , a 5 ) A=(a_3,a_4,a_5) A=(a3,a4,a5) B = ( b 1 , b 2 , b 3 ) B=(b_1,b_2,b_3) B=(b1,b2,b3)
  3. 此时两个序列的中位数分别为 a 4 a_4 a4 b 2 b_2 b2,由于 a 4 > b 2 a_4 > b_2 a4>b2,所以得到序列 A = ( a 3 , a 4 ) A=(a_3,a_4) A=(a3,a4) B = ( b 2 , b 3 ) B=(b_2,b_3) B=(b2,b3)
  4. 继续算法我们得到两个序列为 A = ( a 3 ) A=(a_3) A=(a3) B = ( b 3 ) B=(b_3) B=(b3)。此时算法结束,中位数为 a 3 a_3 a3,结果正确。
  5. 如果这里 a 3 = b 3 a_3=b_3 a3=b3,毫无疑问,结论也是正确的。

  我们已经看到了该算法的正确性,现在我们需了解这种算法为什么可以在不合并两个序列的情况下,找到两个序列的中位数。先讨论两个序列中位数不相等的情况,假如两个序列 A A A B B B的中位数 a < b a<b a<b,我们舍弃了相同数量的在序列 A A A中比 a a a小的元素和在序列 B B B中比 b b b大的元素。在这里我们需要从中学知识中的找中位数的思维中跳出来,即两端划去一个最大值和一个最小值,直到只剩中位数。我们可以知道,在序列中我们只要划去了相同数量的比中位数大的元素和比中位数小的元素,那么中位数的位置是不变的,即中位数不变,例如上面的例子中,我们第一步相当于将序列 C C C中划去了比中位数小的元素 a 1 , a 2 a_1,a_2 a1,a2和比中位数大的元素 b 4 , b 5 b_4,b_5 b4,b5,此时序列 C C C的中位数依旧是 a 3 a_3 a3

  那么问题来了,我们这里算法的操作是划去序列 A A A中比 A A A中位数小的元素和序列 B B B中比 B B B中位数小的元素,而并不是在序列 C C C中划去元素。此时我们只需要证明,两个序列的中位数其实是处于两个序列各自中位数之间即可,即 a 3 ≤ c ≤ b 3 a_3 \leq c \leq b_3 a3cb3,那么此时我们在两个序列中划去两边的元素就相当于从序列 C C C中划去相同数量的大小元素。证明如下:
  假设序列 A A A B B B的中位数为 a a a b b b,两个序列的长度均为 n n n,两个序列的中位数为 c c c
  设 c < m i n ( a , b ) c<min(a,b) c<min(a,b)或者 c > m a x ( a , b ) c>max(a,b) c>max(a,b),即 c c c不处于 a a a b b b之间。我们只证明小于的一边,大于同理。
  此时,那么 A A A B B B共同序列中小于元素 c c c的个数最多为 2 × ⌊ n / 2 − 1 ⌋ 2\times\lfloor {n/2 - 1} \rfloor 2×n/21 n − 2 n-2 n2个,而大于 c c c的元素却有 n + 1 n+1 n+1个,明显不符合中位数的定义,所以假设不成立,即 c c c应该处于 a a a b b b之间。

  最后我们再来看 a = b a=b a=b的情况,此时如果两个序列的中位数相等,那么说明两个中位数隔开的两个序列的大小部分正好数量相等,且都是分布在 a a a b b b两侧,显然 a a a或者 b b b就是中位数。


代码实现:

int M_Search(int A[],int B[],int n){
    int s1=0,s2=0,m1,m2,e1=n-1,e2=n-1;
    //分别表示A,B的首位数、末位数、中位数
    while(s1!=e1||s2!=e2){
        m1=(s1+e1)/2;
        m2=(s2+e2)/2;
        if(A[m1]==B[m2])
            return A[m1];
        if(A[m1]<B[m2]){
            if((e1+d1)%2==0){
                s1=m1;
                e2=m2;
            }
            else{
                s1=m1+1;
                e2=m2;
            }
        }
        else{
            if((e2+d2)%2==0){
                e1=m1;
                s2=m2;
            }
            else{
                e1=m1;
                s2=m2+1;
            }
        }
    }
    return A[s1]<B[s2]?A[s1]:B[s2];
}

注:此代码为2021年王道老师考研辅导书《数据结构考研复习指导》原代码,并未实战中跑过。

©️2020 CSDN 皮肤主题: 游动-白 设计师:上身试试 返回首页