題目
一個長度為 L 的升序序列 S,處在第個位置的數稱為 S 的中位數。例如,若序列
,則
的中位數是 15,兩個序列的中位數是含他們所有元素的升序序列的中位數。例如,若
,則
和
的中位數是 11。現在有兩個等長升序序列 A 和 B,試設計一個算法,找出兩個序列 A 和 B 的中位數。
分析
暴力
把 A 和 B 混合到一起再找中位數。實現方法簡單且不為本篇文章討論重點,不再詳細敘述。
減治
將升序序列左右兩邊同時減去相等個數的數字,中位數不變。
令兩個升序序列 A , B 的中位數為 a , b,求解過程如下:
① 若 a = b,則 a 或 b 即為所求中位數。
② 若 a < b,則舍棄序列 A 中較小的一半,同時舍棄序列 B 中較大的一半,要求兩次舍棄的長度相等。
③ 若 a > b,則舍棄序列 A 中較大的一半,同時舍棄序列 B 中較小的一半,要求兩次舍棄的長度相等。
在保留的兩個升序序列中,重復過程 ① ,② ,③,直到兩個序列中均只含一個元素時為止,較小者即為所求的中位數。
① a = b
a = b時,容易得到兩個序列在數軸上的大小關系,雖然左右兩邊並沒有絕對大小關系,但是中位數的選取只跟中間位置有關,所以並不影響。
② a != b
a != b時,以 a < b 為例。
對於奇數個數序列:
因為中位數只與升序序列的位置有關,通過左圖中的有/無絕對大小關系可得 藍1,藍2,綠4,綠5 代表的四個數絕對不可能在中間位置(5),所以把他們刪去,即中位數的大小范圍為 [a , b] 。
對於偶數個數序列:
還是那句話因為中位數只與升序序列的位置有關,通過右圖中的有/無絕對大小關系可得 藍1,藍2,綠3,綠4 代表的四個數絕對不可能在中間位置(4),所以把他們刪去,即中位數的大小范圍為 ( a , b ]。
實現
int M_Search(int A[], int B[], int n) { int s1=0,d1=n-1,m1,s2=0,d2=n-2,m2;//A,B序列的首位數、中位數、末位數
while (s1 != d1 || s2 != d2) { m1=(s1+d1)/2; m2=(s2+d2)/2; if (A[m1]==B[m2]) //當兩個中位數相等,即為所求中位數
return A[m1]; else if (A[m1]<B[m2]) { if((s1+d1)%2==0) //當元素個數為奇數
{ s1=m1; //舍棄A中間點以前的部分且保留中間點
d2=m2; //舍棄B中間點以后的部分且保留中間點
} else //當元素個數為偶數
{ s1=m1+1; //舍棄A中間點及中間點以前的部分
d2=m2; //舍棄B中間點以后的部分且保留中間點
} } else //同理
{ if ((s2+d2)%2==0) { d1=m1; s2=m2; } else { d1=m1; s2=m2+1; } } } return A[s1]<B[s2]?A[s1]:B[s2]; }