LeetCode 04寻找两个正序数组的中位数(困难)二分法
題目描述:
嘔心瀝血的一個題解,點(diǎn)贊關(guān)注收藏,一鍵三聯(lián),一起加入我們打卡!
題目描述:
給定兩個大小為 m 和 n 的正序(從小到大)數(shù)組 nums1 和 nums2。
請你找出這兩個正序數(shù)組的中位數(shù),并且要求算法的時間復(fù)雜度為 O(log(m + n))。
你可以假設(shè) nums1 和 nums2 不會同時為空。
示例 1:
nums1 = [1, 3]
nums2 = [2]
則中位數(shù)是 2.0
示例 2:
nums1 = [1, 2]
nums2 = [3, 4]
則中位數(shù)是 (2 + 3)/2 = 2.5
歸并法(O(m+n))
分析之前小吐槽一句,這題自己真的沒想到O(log(m+n))的方法,只能想到O(m+n)的歸并,沒想到怎么去使用二分,后來看了題解也是才明白。但也算自己理解了和大家分享一下。
對于這個問題或許本身不難,但是可能難在O(log(m+n))的時間復(fù)雜度上。
如果真的無法想到好的方法,先想著過關(guān),該用什么方法呢?
法一暴力法:
可以將兩個數(shù)組添加到一個總的數(shù)組中,然后給這個數(shù)組進(jìn)行排序。正常的排序是**O(nlogn)**的時間復(fù)雜度。排序之后根據(jù)奇數(shù)偶數(shù)取中位數(shù)即可。
法二歸并法:
給的兩個數(shù)組本身是有序的,想必熟悉歸并排序的朋友們應(yīng)該能一下就想出來這個方法,兩個有序的.只需按照以下流程即可完成歸并:
-
待歸并的兩個數(shù)組分別設(shè)置兩個指針leftindex,rightindex均從0開始。新數(shù)組也設(shè)置游標(biāo)index。
-
比較兩數(shù)組leftindex和rightindex位置的值,較小的那個賦值到新數(shù)組中同時新數(shù)組游標(biāo)和較小的那個游標(biāo)均加一。
-
重復(fù)到其中一個數(shù)組遍歷完,另一個數(shù)組剩余值直接加入后面即可。
實(shí)現(xiàn)代碼:
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))的時間復(fù)雜度,估計大部分人都能想到二分,我當(dāng)時也是一樣,但是該怎么想呢這就是一個問題。記錄下我當(dāng)初錯誤的想法:
二分,二分找到兩個中間的。然后正常有個長的,有個短的,根據(jù)兩個數(shù)值比較分類推測中位數(shù)應(yīng)該在哪個區(qū)間……然后大腦就斷電了。
對于中位數(shù)的簡單分析:
- 如果兩個數(shù)組長度和為奇數(shù),那么最終這個中位數(shù)是由一位數(shù)確定的。
- 如果兩個數(shù)組長度和為偶數(shù),那么最終這個中位數(shù)是由兩位數(shù)取平均值確定的。
對兩個數(shù)組的簡單分析:
- 兩個數(shù)組應(yīng)該有一個長一點(diǎn),另一個點(diǎn)一點(diǎn)(等長也不影響)。
- 中位數(shù)可能讓兩個數(shù)組都分成兩部分:一部分小于中位數(shù),一部分大于中位數(shù)。但兩個部分合起來總數(shù)量應(yīng)該一致。
對兩數(shù)組和中位數(shù)位置分析:
- 我們知道兩數(shù)組雖然可能等長(不影響),但正常情況應(yīng)該是一個長(m)一個短(n)。長短數(shù)組分別對應(yīng)的坐標(biāo)m1和n1和中位數(shù)坐標(biāo)有什么關(guān)系?
- 無論總和奇數(shù)偶數(shù),都滿足(m1+n1)=(m+n)/2;因為兩個數(shù)組都是有序的所以總共小于中位數(shù)的占一半。其中m和n是定值。也就是不管你怎么變動,這兩個坐標(biāo)編號是總和為定值得!
如何分析為定值得坐標(biāo)
- 既然兩個坐標(biāo)的總和為定值,那么可不可以把其中一個當(dāng)為自變量,一個看成自變量呢?比如x+y=5你不好分析但是y=5-x,你分析x同時y就確定了。對吧?
- 那么選擇長的那個作為變量還是短的那個作為變量呢?短的,為啥,主要因為如果從長的當(dāng)成變量咱們有些區(qū)域無法對應(yīng)到短的(因為長度即使加上短的所有也到不了一半,處理起來麻煩):
- 但是短的就可以很好避免這種情況:
所以我們就用二分去查找小的這個區(qū)間,找到最終的結(jié)果,你可能會問:什么樣情況能夠滿足確定這條線的附近就是產(chǎn)生中位數(shù)的?
- 二分進(jìn)行查找編號的時候,滿足左側(cè)都比線右側(cè)小才行。這種情況在二分查找就是一個平衡的結(jié)果。
最后找到這個index線了。取值比較你還要有注意的地方:
- 取左側(cè)的時候左側(cè)如果有index為0,取右側(cè)的時候index為最大值:
- 所以這種在你最后取值的時候,需要考慮左右側(cè)是否有值。同時取長的那個也要比較,因為可能出現(xiàn)等長情況例如:1 2 3 4,和5 6 7 8這種去到臨界。需要判斷當(dāng)然在實(shí)現(xiàn)過程用三目運(yùn)算簡化!
總的來說:
- 根據(jù)短的進(jìn)行二分查找位置,先找到線index,說明中位數(shù)在附近產(chǎn)生。(奇數(shù)偶數(shù)在查找因為要除2可以通用表達(dá)式)
- 如果總個數(shù)奇數(shù),那么就是線左側(cè)最大的那個(兩個比較或只有一個)
- 如果總個數(shù)偶數(shù),那么就是線左側(cè)最大的那個(兩個比較或只有一個)和線右側(cè)最小的那個(兩個比較或只有一個)的值取平均,注意是double類型。
- 其他注意點(diǎn),搞清index從0開始,搞清邏輯上的第幾個和數(shù)組顯示使用的第幾個的index的區(qū)別。
附上代碼:
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;//理論中位數(shù)滿足的位置int left=0,right=nums1.length;//二分查找短的while (left<right) {//找到對應(yīng)位置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);//左側(cè)最大和右側(cè)最小那個先算出來再說,根據(jù)奇偶再使用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;} }結(jié)語
本次打卡結(jié)束,再接再勵。
至于其他方法暫時先不學(xué)了,感覺這題還是挺有難度的,需要搞明白要點(diǎn)時候。
最后我請你們兩連事幫忙一下:
點(diǎn)贊、關(guān)注一下支持, 您的肯定是我在平臺創(chuàng)作的源源動力。
微信搜索「bigsai」,關(guān)注我的公眾號,不僅免費(fèi)送你電子書,我還會第一時間在公眾號分享知識技術(shù)。加我還可拉你進(jìn)力扣打卡群一起打卡LeetCode。
記得關(guān)注、咱們下次再見!
總結(jié)
以上是生活随笔為你收集整理的LeetCode 04寻找两个正序数组的中位数(困难)二分法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【排序算法】计数排序引发的围观风波——一
- 下一篇: LeetCode 05最长回文子串