DS-007 順序表-尋找兩個序列的中位數
阿新 • • 發佈:2018-12-12
題目:一個長度為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]; }
解析:
- n為陣列長度,A、B長度相等,A、B最大下標n-1,合併後C的最大下標2n-1
- for迴圈初始化i,j為0。入口條件是i、j同時小於n,即遍歷完一個數組就退出迴圈。另一個數組的剩餘部分由下面的while迴圈執行。
- 將A、B的元素比較,將較小的那個寫到陣列C中。
- C[k] = A[i++];相當於執行C[k] = A[i]; i++;for迴圈會再將k自增1,表示下一次較小的元素存放在C的位置。
- 一個數組遍歷完成後,退出for迴圈。另一個數組的所有元素都比當前C中的元素大,再依次存放陣列C中。
- 最後返回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];
}