1. 程式人生 > >DS-007 順序表-尋找兩個序列的中位數

DS-007 順序表-尋找兩個序列的中位數

題目:一個長度為L(L≥1)的升序序列S,處在第[L/2]個位置的數稱為S的中位數。例如列S1=(11,13,15,17,19),則S1中的中位數是15。兩個序列的中位數是含它們所有元素的升序序列的中位數。例如,若92=(2,4,6,8,20),則.S1和S2的中位數是11。現有兩個等長升序序列A和B,試設計一個在時間和空間兩方面都儘可能高效的演算法,找出兩個序列A和B的中位數。

要求: (1)給出演算法的基本設計思想。

(2)根據設計思想,採用C或C++或Java語言描述演算法,關鍵之處給出註釋。

(3)說明你所設計演算法的時間複雜度和空間複雜度。

答:

(1)演算法思想:將A和B用二路歸併合併排序成C,找C的中位數。

根據題目,序列S為奇數,5個元素,下標(0+4)/2=2,剛好第三個元素下標是2。,S的元素個數為偶數10,(0+9)/2 = 4 ,剛好取第5個元素。A、B等長,合併之後元素個數一定是偶數。

(2)C程式碼:

int Mid_Search(int A[], int B[], int n){
	int C[2n]; //要求分配最多儲存2n個整數的儲存空間,C指向這塊空間
	int i, j, k;
	for(i=0, j=0, k=0; i<n && j<n; k++){
		if(A[i] <= B[j])
			C[k] = A[i++];
		else
			C[k] = B[j++];			
	}
	while (i<n) C[k++] = A[i++];
	while (j<n) C[k++] = B[j++];
	
	return C[(2n-1)/2];
}

解析:

  1. n為陣列長度,A、B長度相等,A、B最大下標n-1,合併後C的最大下標2n-1
  2. for迴圈初始化i,j為0。入口條件是i、j同時小於n,即遍歷完一個數組就退出迴圈。另一個數組的剩餘部分由下面的while迴圈執行。
  3. 將A、B的元素比較,將較小的那個寫到陣列C中。
  4. C[k] = A[i++];相當於執行C[k] = A[i]; i++;for迴圈會再將k自增1,表示下一次較小的元素存放在C的位置。
  5. 一個數組遍歷完成後,退出for迴圈。另一個數組的所有元素都比當前C中的元素大,再依次存放陣列C中。
  6. 最後返回C的中位數,即為所求的中位數。

(3)該演算法時間複雜度為O(n),空間複雜度為O(n)。

以上不是最佳演算法,下面最優演算法太難想。

時間複雜度為O(log2n),空間複雜度為O(1)。

int M_Search(int A[],int B[],int n){
    int s1=0,d1=n-1,m1,s2=0,d2=n-1,m2;
    //分別表示序列A和B的首位數、末尾數和中位數
    while(s1!d1||s2!=d2){
        m1=(s1+d1)/2;
        m2=(s2+d2)/2;
        if(A[m1]==B[m2])
            return A[m1];           //滿足條件1
        if(A[m1]<B[m2]){            //滿足條件2
            if((s1+d1)%2==0){       //若元素個數為奇數
                s1=m1;              //捨棄A中間點以前的部分且保留中間點
                d2=m2;              //捨棄B中間點以後的部分且保留中間點
            }
            else{                   //元素個數為偶數
                s1=m1+1;            //捨棄A中間點及中間點以前部分
                d2=m2;              //捨棄B中間點以後部分且保留中間點
            }
        }
        else {                      //滿足條件3
            if((s2+d2)%2==0){       //若元素個數為奇數
                d1=m1;              //捨棄A中間點以後的部分且保留中間點
                s2=m2;              //捨棄B中間點以前的部分且保留中間點
            }
            else{                   //元素個數為偶數
                d1=m1;              //捨棄A中間點以後部分且保留中間點
                s2=m2+1;            //捨棄B中間點及中間點以前部分
            }
        }
    }
    return A[s1]<B[s2]?A[s1]:B[s2];
}