【LeetCode】4. Median of Two Sorted Arrays

題意

找到兩個(gè)有序數(shù)組的中位數(shù)

解答一(遞歸踢械,時(shí)間復(fù)雜度O(logk))

\color{red}{關(guān)鍵點(diǎn):一步步篩選出k/2個(gè)已經(jīng)確定在k個(gè)最小數(shù)中的元素}\

首先理解題意兩個(gè)關(guān)鍵點(diǎn):有序數(shù)組和中位數(shù)

  • 對于有序數(shù)組a,長度為m
    • 如果m為奇數(shù)魄藕,則中位數(shù)為第m/2+1小的數(shù)内列;
    • 如果m為偶數(shù),則中位數(shù)為第m/2+1小與第m/2小的數(shù)的平均和泼疑;
  • 對于有序數(shù)組ab德绿,長度分別為mn
    • 如果m+n為奇數(shù),則兩個(gè)數(shù)組的中位數(shù)為第(m+n)/2+1小的數(shù)
    • 如果m+n為偶數(shù)退渗,則兩個(gè)數(shù)組的中位數(shù)為第(m+n)/2+1小和第(m+n)/2小的數(shù)平均和移稳;

根據(jù)上面性質(zhì)可知,只要找到第(m+n)/2+1小和第(m+n)/2小的數(shù)即可会油,該題演變成找兩個(gè)有序數(shù)組第k小的數(shù)个粱;

如何尋找兩個(gè)有序數(shù)組的第k小數(shù)?二分查找

先聲明查找第k小數(shù)的函數(shù)

/* param:
 *      a   數(shù)組a起始地址
 *      m   數(shù)組a起始地址開始的長度
 *      b   數(shù)組b起始地址
 *      n   數(shù)組b起始地址開始的長度
 */
int findKth(int* a, int m, int* b, int n, int k);

如果i=k/2翻翩,則j=k-i=k/2, a_0..a_{i-1}b_0..b_{j-1}總共k個(gè)數(shù)都许,但這k個(gè)數(shù)不一定是最小的k個(gè)數(shù)

  • a_{i-1} \lt b_{j-1}
    說明a_{i}的左邊部分肯定在最小的k個(gè)數(shù)里面,a_{i}右邊部分可能存在數(shù)在最小的k個(gè)數(shù)里面嫂冻;
    這時(shí)候需要在a_{i}..a_{m-1}b_0..b_{n-1}中查找第{k-i}小的數(shù)
findKth(a+i, m-i, b, n, k-i);
  • a_{i-1} \gt b_{j-1}
    說明b_{i}的左邊部分肯定在最小的k個(gè)數(shù)里面胶征,b_{i}右邊部分可能存在數(shù)在最小的k個(gè)數(shù)里面;
    這時(shí)候需要在a_{0}..a_{m-1}b_{j}..b_{n-1}中查找第{k-j}小的數(shù)
findKth(a, m, b+j, n-j, k-j);
  • a_{i-1} = b_{j-1}
    說明找到第k小的數(shù)桨仿,a_0..a_{i-1}{b_0..b_{j-1}}總共k個(gè)最小的數(shù)

二分核心流程:

int i = min(m, k/2), j = k-i;
if (a[i-1] < b[j-1]) {
    return findKth(a+i, m-i, b, n, k-i);
} else if (a[i-1] > b[j-1]) {
    return findKth(a, m, b+j, n-j, k-j);
} else {
    return a[i-1];
}

現(xiàn)在考慮邊界情況

  • {i-1} \ge 0{j-1} \ge 0
    =>{i} \ge 1{j} \ge 1
    =>k \ge 2
    所以當(dāng)k=1時(shí)不能進(jìn)入上面二分核心流程
if (k==1) return min(a[0], b[0]);`
  • 由于要使用a[0]b[0]睛低,所以ab必須分別至少有一個(gè)元素;如果a沒有元素或者b沒有元素不能進(jìn)入k==1的判斷條件
if (m == 0) return b[k-1];
if (n == 0) return a[k-1];
  • m \le k/2
    =>{i = m}, j=k-i=k-m \ge 2m-m=m
    => j \lt n => m \lt n
    如果m> n服傍,則需要將數(shù)組ab交換

完整代碼:

class Solution {
public:
    int findKth(int* a, int m, int* b, int n, int k) {
        if (m > n) return findKth(b, n, a, m, k);
        if (m == 0) return b[k-1];
        if (k == 1) return min(a[0], b[0]);
        int i = min(m, k/2), j = k - i;
        if (a[i-1] < b[j-1]) {
            return findKth(a+i, m-i, b, n, k-i);
        } else if (a[i-1] > b[j-1]){
            return findKth(a, m, b+j, n-j, k-j);
        } else {
            return a[i-1];
        }
    }
    
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int m = nums1.size(), n = nums2.size();
        if ((m+n)%2) {
            return findKth(nums1.data(), m, nums2.data(), n, (m+n)/2+1);
        } else {
            int left = findKth(nums1.data(), m, nums2.data(), n, (m+n)/2);
            int right = findKth(nums1.data(), m, nums2.data(), n, (m+n)/2+1);
            cout<<left<<" "<<right<<endl;
            return (left+right)/2.0;
        }
    }
};

解答二(非遞歸钱雷,時(shí)間復(fù)雜度O(log(min(m,n))))

\color{red}{關(guān)鍵點(diǎn):對一個(gè)數(shù)組進(jìn)行二分查找,另一個(gè)數(shù)組相應(yīng)的位置也會被相應(yīng)確定}\
對于數(shù)組a_0..a_{m-1}吹零,有{m+1}種分隔方式罩抗,如下圖(a)所示;當(dāng)aa_{left}部分長度為i時(shí)灿椅,a_{right}部分長度為{m-i}套蒂,如下圖(b)所示钞支;

  • 如果{i=0},則{|a_{left}|=0}操刀,{|a_{right}|=m}
  • 如果i=m伸辟,則{|a_{left}|=m}{|a_{right}|=0}
  • 如果{i \ne0}{i \ne m}馍刮,則{|a_{left}|=i}{|a_{right}|=m-i}

同樣的對于數(shù)組b_0..b_{n-1}窃蹋,有{n+1}種分隔方式卡啰,

  • 如果{j=0},則{|b_{left}|=0}警没,{|b_{right}|=n}
  • 如果j=n匈辱,則{|b_{left}|=n}{|b_{right}|=0}
  • 如果{j \ne0}{j \ne m}杀迹,則{|b_{left}|=j}亡脸,{|b_{right}|=n-j}

如何求數(shù)組ab的中位數(shù)?按照上述劃分原則树酪,只要把數(shù)組ab劃分成leftright兩部分浅碾,保證{|a_{left}|+|b_{left}| = |a_{right}|+|b_{right}| },同時(shí){max({left}) \le min({right})}续语,左邊元素都小于等于右邊元素垂谢,則中位數(shù)為\frac{max({left})+min({right})}{2}

但是數(shù)組分偶數(shù)和奇數(shù)兩種情況:

  • 如果{m+n}是偶數(shù)疮茄,只需要{left}{right}等分即可滥朱,即{i+j=m-i+n-j} => {j=\frac{m+n}{2}-i},又由于整數(shù)除法是向下取值的力试,則\frac{m+n}{2}=\frac{m+n+1}{2}徙邻,故也滿足{j=\frac{m+n+1}{2}-i},中位數(shù)為\frac{max({left})+min({right})}{2}畸裳;
  • 如果{m+n}是奇數(shù)缰犁,將中間元素放到左邊,右邊將比左邊少一個(gè)元素躯畴,即{i+j=m-i+n-j+1} => {j=\frac{m+n+1}{2}-i}民鼓,中位數(shù)為\frac{max({left})}{2}
    \color{red}{注意:當(dāng)m+n是奇數(shù)時(shí),(m+n+1)/2 \ne (m+n)/2}

綜合上述兩個(gè)條件蓬抄,不論m+n是奇數(shù)還是偶數(shù)丰嘉,對于數(shù)組ab中位數(shù)來說滿足j=(m+n+1)/2-i,為了保證{ j \ge 0}嚷缭,則
\begin{aligned} \frac{m+n+1}{2}-i \ge 0 =>{m+n+1} \ge {2i} => {m+n+1} \ge {2i} \ge {2m} => n \ge m-1 => n \gt m \\ \end{aligned}

故中位數(shù)滿足的條件是:

  • j=\frac{m+n+1}{2}-i
  • {b_{j-1}} \le a_{i}{a_{i-1}} \le b_{j}

邊界條件:
當(dāng)m+n不論奇數(shù)偶數(shù)時(shí)

  • 當(dāng)i=0時(shí)饮亏,j=(m+n+1)/2耍贾,則最小的數(shù)都在數(shù)組bmax({left})=b_{j-1}
  • 當(dāng)j=0時(shí)路幸,i=(m+n+1)/2荐开,則則最小的數(shù)都在數(shù)組amax({left})=a_{i-1}
  • 當(dāng)i \ne 0j \ne 0简肴,則max({left})=max(a_{i-1},b_{j-1})

當(dāng)m+n為偶數(shù)時(shí)晃听,需要min(right)

  • 當(dāng)i=m時(shí),數(shù)組a都在左邊砰识,右邊都是b能扒,min({right})=b_{j}
  • 當(dāng)j=n時(shí),數(shù)組b都在左邊辫狼,右邊都是a初斑,min({right})=a_{i}
  • 當(dāng)i \ne mj \ne n,則min({right})=min(a_{i},b_{j})

當(dāng)對數(shù)組a進(jìn)行二分查找位置i時(shí)膨处,相應(yīng)的數(shù)組b位置j=(m+n+1)/2-i见秤,此時(shí)數(shù)組a和數(shù)組b左右兩部分相等,中位數(shù)
media=\left\{ \begin{aligned} \frac{max({left})+min({right})}{2} & & m+n偶數(shù) \\ \frac{max({left})}{2} & & m+n奇數(shù) \\ \end{aligned} \right.

完整代碼:

class Solution {
public:    
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int m = nums1.size(), n = nums2.size();
        if (m > n) return findMedianSortedArrays(nums2, nums1);
        int imin = 0, imax = m, i = 0, j = 0, media = 0;
        while (imin <= imax) {
            i = (imin+imax)/2;
            j = (m+n+1)/2 - i;
            if (j-1>=0 && j-1<n && i>=0 && i<m  && nums2[j-1] > nums1[i]) {
                imin = i + 1;
            } else if (i-1>=0 && i-1<m && j>=0 && j< n  && nums1[i-1] > nums2[j]) {
                imax = i - 1;
            } else {    
                if (i == 0) media = nums2[j-1];
                else if (j == 0) media = nums1[i-1];
                else media = max(nums1[i-1], nums2[j-1]);
                break;
            }
        }
        if ((m+n)&1) return media;
        if (i == m) media += nums2[j];
        else if (j == n) media += nums1[i];
        else media += min(nums1[i], nums2[j]);
        return media*0.5;
    }
};
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末真椿,一起剝皮案震驚了整個(gè)濱河市鹃答,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌突硝,老刑警劉巖挣跋,帶你破解...
    沈念sama閱讀 218,451評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異狞换,居然都是意外死亡避咆,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,172評論 3 394
  • 文/潘曉璐 我一進(jìn)店門修噪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來查库,“玉大人,你說我怎么就攤上這事黄琼》” “怎么了?”我有些...
    開封第一講書人閱讀 164,782評論 0 354
  • 文/不壞的土叔 我叫張陵脏款,是天一觀的道長围苫。 經(jīng)常有香客問我,道長撤师,這世上最難降的妖魔是什么剂府? 我笑而不...
    開封第一講書人閱讀 58,709評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮剃盾,結(jié)果婚禮上腺占,老公的妹妹穿的比我還像新娘淤袜。我一直安慰自己,他們只是感情好衰伯,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,733評論 6 392
  • 文/花漫 我一把揭開白布铡羡。 她就那樣靜靜地躺著,像睡著了一般意鲸。 火紅的嫁衣襯著肌膚如雪烦周。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,578評論 1 305
  • 那天怎顾,我揣著相機(jī)與錄音论矾,去河邊找鬼。 笑死杆勇,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的饱亿。 我是一名探鬼主播蚜退,決...
    沈念sama閱讀 40,320評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼彪笼!你這毒婦竟也來了钻注?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,241評論 0 276
  • 序言:老撾萬榮一對情侶失蹤配猫,失蹤者是張志新(化名)和其女友劉穎幅恋,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體泵肄,經(jīng)...
    沈念sama閱讀 45,686評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡捆交,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,878評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了腐巢。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片品追。...
    茶點(diǎn)故事閱讀 39,992評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖冯丙,靈堂內(nèi)的尸體忽然破棺而出肉瓦,到底是詐尸還是另有隱情,我是刑警寧澤胃惜,帶...
    沈念sama閱讀 35,715評論 5 346
  • 正文 年R本政府宣布泞莉,位于F島的核電站,受9級特大地震影響船殉,放射性物質(zhì)發(fā)生泄漏鲫趁。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,336評論 3 330
  • 文/蒙蒙 一利虫、第九天 我趴在偏房一處隱蔽的房頂上張望饮寞。 院中可真熱鬧孝扛,春花似錦、人聲如沸幽崩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,912評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽慌申。三九已至陌选,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蹄溉,已是汗流浹背咨油。 一陣腳步聲響...
    開封第一講書人閱讀 33,040評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留柒爵,地道東北人役电。 一個(gè)月前我還...
    沈念sama閱讀 48,173評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像棉胀,于是被迫代替她去往敵國和親法瑟。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,947評論 2 355

推薦閱讀更多精彩內(nèi)容