題目
給定兩個大小爲 m 和 n 的有序數組 nums1 和 nums2。請你找出這兩個有序數組的中位數,並且要求算法的時間複雜度爲 O(log(m + n))。你可以假設 nums1 和 nums2 不會同時爲空。
示例 1:
nums1 = [1, 3] nums2 = [2]
則中位數是 2.0
示例 2:
nums1 = [1, 2] nums2 = [3, 4]
則中位數是 (2 + 3)/2 = 2.5
思路一 歸併排序
此題是兩個有序數組中查找第K小的數的特例,而我能想到的查找第K個元素便是使用歸併排序算法(鄧公的<<數據結構>>講的非常不錯)。所以,一開始我是按照歸併排序的思路+第K個元素停止的來做的。但一開始我沒想到可以把整個查找函數提取出來進行封裝,當時寫的有點複雜。
在查看一些解題思路後,得到啓發,把查找函數提出來後,代碼易讀性與簡潔性得到了很大的提升。
關於時間複雜度,題目要求O(log(n+m)),但此思路的時間複雜度爲O(n+m),一般情況下的時間複雜度默認最壞時間複雜度。因此在時間複雜度的要求上,這個思路是不滿足的。但從提交的結果來看,算法實際消耗的時間還是可以令人滿足的。
代碼
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int xSize=nums1.size();
int ySize=nums2.size();
int total = xSize+ySize;
if((xSize+ySize)&0x01) return find_Kth(nums1,xSize,nums2,ySize,total/2+1);
else return (find_Kth(nums1,xSize,nums2,ySize,total/2)+find_Kth(nums1,xSize,nums2,ySize,total/2+1))/2;
}
private:
double find_Kth(vector<int>& nums1,int xSize,vector<int>& nums2,int ySize,int target){
int xIndex=0;
int yIndex=0;
int loken=0;
double value;
while(xIndex<xSize||yIndex<ySize){
if((xIndex<xSize)&&(!(yIndex<ySize)||nums1[xIndex]<=nums2[yIndex])){
value=nums1[xIndex];
xIndex++;
loken++;
if(loken==target) return value;
}
if((yIndex<ySize)&&(!(xIndex<xSize)||(nums2[yIndex]<nums1[xIndex]))){
value=nums2[yIndex];
yIndex++;
loken++;
if(loken==target) return value;
}
}
return 0;
}
};
結果
思路二
設定ia,ib。其中ia、ib滿足一下條件:
- m<k/2時,ia=m;否則ia=k/2
- ib+ia=k,ib=k-ia
ia指示的是A中前ia個元素,ib指示的是B中前ib個元素。
當A[ia]=B[ib]時,因爲ia+ib=k,所以第k個元素=A[ia]=B[ib],返回A[ia]或B[ib];
當A[ia]<B[ib]時,可以確定A中前ia個元素,一定是前k/2小的元素。B[ib]<A[ia]時,B中前ib個元素,一定是前k/2小的元素。所以可以遞歸調用查找函數,修改一定的條件。
這個算法的思路實現並不算難,但是難就難在了邊界條件的確定。目前對於這一塊,我並沒有很深入的理解,可能需要一定的題量支持。就對三個邊界發生的情況做下解釋:
- 第一個是說,A的範圍必須比B的範圍小。因爲ia取值時,考慮了A的範圍,但是B取值時,並沒有考慮。
- 第二個是說,A爲空,但A爲空有兩種可能,一種是輸入時就是空的,另一種是,在執行過程中,A的範圍不斷變小而發生的。
- 第三個是說,k=1。這個也是有兩種,與上述情況類似。
代碼
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
const int m=nums1.size();
const int n=nums2.size();
int total=m+n;
if(total&0x01)
return find_Kth(nums1.begin(),m,nums2.begin(),n,total/2+1);
else
return (find_Kth(nums1.begin(),m,nums2.begin(),n,total/2+1)+find_Kth(nums1.begin(),m,nums2.begin(),n,total/2))/2;
}
private:
double find_Kth(vector<int>::const_iterator A,int m,vector<int>::const_iterator B,int n,int k){
if(m>n) return find_Kth(B,n,A,m,k);
if(m==0) return *(B+k-1);
if(k==1) return min(*A,*B);
int ia=min(k/2,m);
int ib=k-ia;
if(*(A+ia-1)<*(B+ib-1)) return find_Kth(A+ia,m-ia,B,n,k-ia);
else if(*(B+ib-1)<*(A+ia-1)) return find_Kth(A,m,B+ib,n-ib,k-ib);
else return A[ia-1];
}
};