LeetCode 04尋找兩個正序數組的中位數(困難)二分法web
題目描述:
嘔心瀝血的一個題解,點贊關注收藏,一鍵三聯,一塊兒加入咱們打卡!算法
題目描述:json
給定兩個大小爲 m 和 n 的正序(從小到大)數組 nums1 和 nums2。
請你找出這兩個正序數組的中位數,而且要求算法的時間複雜度爲 O(log(m + n))。
你能夠假設 nums1 和 nums2 不會同時爲空。數組
示例 1:微信
nums1 = [1, 3]
nums2 = [2]
則中位數是 2.0app
示例 2:編輯器
nums1 = [1, 2]
nums2 = [3, 4]
則中位數是 (2 + 3)/2 = 2.5url
歸併法(O(m+n))
分析以前小吐槽一句,這題本身真的沒想到O(log(m+n))的方法,只能想到O(m+n)的歸併,沒想到怎麼去使用二分,後來看了題解也是才明白。但也算本身理解了和你們分享一下。spa
對於這個問題或許自己不難,可是可能難在O(log(m+n))的時間複雜度上。.net
若是真的沒法想到好的方法,先想着過關,該用什麼方法呢?
法一暴力法:
能夠將兩個數組添加到一個總的數組中,而後給這個數組進行排序。正常的排序是O(nlogn)的時間複雜度。排序以後根據奇數偶數取中位數便可。
法二歸併法:
給的兩個數組自己是有序的,想必熟悉歸併排序的朋友們應該能一下就想出來這個方法,兩個有序的.只需按照如下流程便可完成歸併:
待歸併的兩個數組分別設置兩個指針leftindex,rightindex均從0開始。新數組也設置遊標index。
比較兩數組leftindex和rightindex位置的值,較小的那個賦值到新數組中同時新數組遊標和較小的那個遊標均加一。
在這裏插入圖片描述 重複到其中一個數組遍歷完,另外一個數組剩餘值直接加入後面便可。
在這裏插入圖片描述
實現代碼:
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int a[]=new int[nums1.length+nums2.length];
int lindex=0,rindex=0;
int index=0;
while (lindex<nums1.length&&rindex<nums2.length) {
if(nums1[lindex]<nums2[rindex])
{
a[index++]=nums1[lindex++];
}
else {
a[index++]=nums2[rindex++];
}
}
while (lindex<nums1.length) {
a[index++]=nums1[lindex++];
}
while (rindex<nums2.length) {
a[index++]=nums2[rindex++];
}
if(a.length%2==0)
return (double)(a[a.length/2-1]+a[a.length/2])/2;
else {
return a[a.length/2];
}
}
二分法(O(log(m+n)))
想到有序的,又是O(log(m+n))的時間複雜度,估計大部分人都能想到二分,我當時也是同樣,可是該怎麼想呢這就是一個問題。記錄下我當初錯誤的想法:
二分,二分找到兩個中間的。而後正常有個長的,有個短的,根據兩個數值比較分類推測中位數應該在哪一個區間……而後大腦就斷電了。
對於中位數的簡單分析:
若是兩個數組長度和爲奇數,那麼最終這個中位數是由一位數肯定的。
若是兩個數組長度和爲偶數,那麼最終這個中位數是由兩位數取平均值肯定的。
對兩個數組的簡單分析:
兩個數組應該有一個長一點,另外一個點一點(等長也不影響)。
中位數可能讓兩個數組都分紅兩部分:一部分小於中位數,一部分大於中位數。但兩個部分合起來總數量應該一致。
在這裏插入圖片描述
對兩數組和中位數位置分析:
咱們知道兩數組雖然可能等長(不影響),但正常狀況應該是一個長(m)一個短(n)。長短數組分別對應的座標m1和n1和中位數座標有什麼關係?
不管總和奇數偶數,都知足(m1+n1)=(m+n)/2;由於兩個數組都是有序的因此總共小於中位數的佔一半。其中m和n是定值。也就是無論你怎麼變更,這兩個座標編號是總和爲定值得!
如何分析爲定值得座標
既然兩個座標的總和爲定值,那麼可不能夠把其中一個當爲自變量,一個當作自變量呢?好比x+y=5你很差分析可是y=5-x,你分析x同時y就肯定了。對吧?
那麼選擇長的那個做爲變量仍是短的那個做爲變量呢?短的,爲啥,主要由於若是從長的當成變量我們有些區域沒法對應到短的(由於長度即便加上短的全部也到不了一半,處理起來麻煩):
在這裏插入圖片描述 可是短的就能夠很好避免這種狀況:
在這裏插入圖片描述
因此咱們就用二分去查找小的這個區間,找到最終的結果,你可能會問:什麼樣狀況可以知足肯定這條線的附近就是產生中位數的?
二分進行查找編號的時候,知足左側都比線右側小才行。這種狀況在二分查找就是一個平衡的結果。
在這裏插入圖片描述
最後找到這個index線了。取值比較你還要有注意的地方:
取左側的時候左側若是有index爲0,取右側的時候index爲最大值:
在這裏插入圖片描述 因此這種在你最後取值的時候,須要考慮左右側是否有值。同時取長的那個也要比較,由於可能出現等長狀況例如:
1 2 3 4
,和5 6 7 8
這種去到臨界。須要判斷固然在實現過程用三目運算簡化!
總的來講:
根據短的進行二分查找位置,先找到線index,說明中位數在附近產生。(奇數偶數在查找由於要除2能夠通用表達式)
若是總個數奇數,那麼就是線左側最大的那個(兩個比較或只有一個)
若是總個數偶數,那麼就是線左側最大的那個(兩個比較或只有一個)和線右側最小的那個(兩個比較或只有一個)的值取平均,注意是double類型。
其餘注意點,搞清index從0開始,搞清邏輯上的第幾個和數組顯示使用的第幾個的index的區別。
附上代碼:
public static double findMedianSortedArrays2(int[] nums1, int[] nums2) {
if(nums1.length>nums2.length)//保證num1長度小,若是不小我交換一下
{
int team[]=nums2.clone();
nums2=nums1;
nums1=team;
}
int k=(nums1.length+nums2.length+1)/2;//理論中位數知足的位置
int left=0,right=nums1.length;//二分查找短的
while (left<right) {//找到對應位置
int m1=(left+right)/2;//在短的位置
int m2=k-m1;//在長的第幾個
//System.out.println(m1+" "+m2);
if(nums1[m1]<nums2[m2-1])//left右移
left=m1+1;
else {//right左移
right=m1;
}
}
//System.out.println(left+" "+k);
//左側最大和右側最小那個先算出來再說,根據奇偶再使用
double leftbig= Math.max(left==0?Integer.MIN_VALUE:nums1[left-1], k-left==0?Integer.MIN_VALUE:nums2[k-left-1]);
double rightsmall=Math.min(left==nums1.length?Integer.MAX_VALUE:nums1[left],k-left==nums2.length?Integer.MAX_VALUE:nums2[k-left]);
//System.out.println(rightsmall);
if((nums1.length+nums2.length)%2==0)
{
return (leftbig+rightsmall)/2;
}
else {
return leftbig;
}
}
結語
本次打卡結束,再接再勵。
至於其餘方法暫時先不學了,感受這題仍是挺有難度的,須要搞明白要點時候。
最近往期精彩回顧:
據說長得俊的更喜歡給人點贊在看關注,關注公衆號:bigsai
,回覆加羣,我們一塊兒打卡,目前已經一百多位csdn朋友加入打卡。
長按識別
關注咱們


本文分享自微信公衆號 - bigsai(bigsai)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。