1. 程式人生 > >求兩個升序序列的中位數

求兩個升序序列的中位數

這裡涉及到資料結構中順序表的實現、刪除、插入、查詢等知識,請檢視:資料結構 -> 線性表
問題描述:

一個長度為L (L>=1)的升序序列S,處在第[L/2]個位置的數稱為S的中位數。例如,若序列S1=(11, 13, 15, 17, 19),則S1的中位數是15,兩個序列的中位數是含它們所有元素的升序序列的中位數。例如,若S2= (2, 4,6,8, 20),則S1和S2的中位數是11。現在有兩個等長升序序列A和B,試設計一個在時間和空間兩方面都儘可能高效的演算法,找出兩個序列A和B的中位數。要求:
1) 給出演算法的基本設計思想。
2) 根據設計思想,釆用C或C++或Java語言描述演算法,關鍵之處給出註釋。
3) 說明你所設計演算法的時間複雜度和空間複雜度。

該題為2011年研究生考試計算機聯考真題。
問題解答:

(1)演算法的基本設計思想如下:

分別求兩個升序序列A、B的中位數,設為a和b,求序列A、B的中位數過程如下:

1) 若a=b,則a或b即為所求中位數,演算法結束。

2) 若a<b,則捨棄序列A中較小的一半,同時捨棄序列B中較大的一半,要求兩次捨棄的長度相等。

3) 若a>b,則捨棄序列A中較大的一半,同時捨棄序列B中較小的一半,要求兩次捨棄的長度相等。

在保留的兩個升序序列中,重複過程1)、2)、3),直到兩個序列中均只含一個元素時為止,較小者即為所求的中位數。

(2)本題程式碼如下:

int M_Search(int A[], int B[], int n) 
{
	int start1 = 0, end1 = n - 1, m1, start2 = 0, end2 = n - 1, m2;
	//分別表示序列A和B的首位數、末位數和中位數

	while (start1 != end1 || start2 != end2)
	{
		m1 = (start1 + end1) / 2;
		m2 = (start2 + end2) / 2;
		if (A[m1] == B[m2])
			return A[m1];   //滿足條件 1)

		if (A[m1]<B[m2]) // 滿足條件 2)
		{  
			if ((start1 + end1) % 2 == 0)  //若元素個數為奇數
			{  
				start1 = m1;  //捨棄A中間點以前的部分且保留中間點
				end2 = m2;  //捨棄B中間點以後的部分且保留中間點
			}
			else				//元素個數為偶數
			{ 
				start1 = m1 + 1;  //捨棄A中間點及中間點以前部分
				end2 = m2;  //捨棄B中間點以後部分且保留中間點
			}
		}
		else
		{  //滿足條件3)
			if ((start2 + end2) % 2 == 0)   //若元素個數為奇數
			{ 
				end1 = m1;    //捨棄A中間點以後的部分且保留中間點
				start2 = m2;    //捨棄B中間點以前的部分且保留中間點
			}
			else     //元素個數為偶數
			{  
				end1 = m1;    //捨棄A中間點以後部分且保留中間點
				start2 = m2 + 1;    //捨棄B中間點及中間點以前部分
			}
		}
	}
	return  A[start1]<B[start2] ? A[start1] : B[start2];
}


(3)演算法的時間複雜度為O(log2n),空間複雜度為O(1)。