面試題3:找到數(shù)組中重復(fù)的數(shù)字

題目1:在一個(gè)長(zhǎng)度為N的數(shù)組里艇纺,所有數(shù)字都在[0, N-1]的范圍內(nèi)。數(shù)組中某些數(shù)字是重復(fù)的完疫,但是不知道有幾個(gè)數(shù)字重復(fù)了,也不知道每個(gè)數(shù)字重復(fù)了幾次债蓝。請(qǐng)找出數(shù)組的任意一個(gè)重復(fù)的數(shù)字壳鹤。

解析:在這道題的背景下,如果把數(shù)字放到他的下標(biāo)對(duì)應(yīng)的位置饰迹,也就是把一個(gè)數(shù)組還原成一個(gè)遞增數(shù)列芳誓,只不過每一個(gè)下標(biāo)對(duì)應(yīng)的數(shù)字就是下標(biāo)本身的話,如果這個(gè)數(shù)組里面沒有重復(fù)數(shù)字啊鸭,那么還原可以完美的完成锹淌。但是由于數(shù)組內(nèi)有重復(fù)數(shù)字,那么在還原的過程中赠制,就會(huì)發(fā)現(xiàn)當(dāng)需要把它放在合適的位置的時(shí)候赂摆,這個(gè)位置上已經(jīng)有一個(gè)相同的數(shù)字了。例如 {0,1,2,2,4,5,6}钟些,下標(biāo)為 3 的 2 應(yīng)該放在下標(biāo)為 2 的位置上烟号,但是這個(gè)地方已經(jīng)有一個(gè) 2 了。遇到這種情況厘唾,我們就立即可以返回這個(gè)數(shù)字褥符。

使用這個(gè)思路龙誊,我們就可以寫出算法了抚垃。只要當(dāng)前位置上的數(shù)字和下標(biāo)不相等,那就看看這個(gè)數(shù)字應(yīng)該去的地方有沒有同樣的數(shù)字趟大。如果發(fā)現(xiàn)那個(gè)地方的數(shù)字和下標(biāo)是相同的鹤树,那就說明這個(gè)地方的數(shù)字是重復(fù)的,就可以返回了逊朽。如果那邊的也不對(duì)的話罕伯,就把當(dāng)前位置上的數(shù)字放到它應(yīng)該的位置,把那個(gè)位置上的數(shù)字交換過來叽讳。然后繼續(xù)比較當(dāng)前位置上的數(shù)字和下標(biāo)追他,直到當(dāng)前位置上的數(shù)字和下標(biāo)相等。接著對(duì)下一個(gè)位置進(jìn)行同樣的處理岛蚤。如果每一位都是合適的邑狸,那就說明這個(gè)數(shù)組沒有重復(fù)數(shù)字。

上面的算法是沒有問題的涤妒,但是你可能有疑問:如果這個(gè)位置永遠(yuǎn)都換不來那個(gè)合適的數(shù)字呢单雾?例如,{1,3,2,4,3} 沒有 0 這個(gè)數(shù)字,那么怎么辦呢硅堆?答案是不影響屿储。檢查第 0 位的時(shí)候,會(huì)把第 0 位的 1 和第 1 位的 3 互換渐逃。這個(gè)時(shí)候第 1 位就是 1够掠,沒毛病。接著會(huì)把第 0 位的 3 和第 3 位的 4 互換朴乖,這個(gè)時(shí)候第 3 位也是 3祖屏,沒毛病。接著會(huì)把第 0 位的 4 和第 4 位的 3 互換买羞,發(fā)現(xiàn)第 3 位已經(jīng)是 3 了袁勺,此時(shí)返回 3⌒笃眨看到?jīng)]有期丰?即便換不到該有的數(shù)字,在交換的過程中也是可以找到重復(fù)的數(shù)字的吃挑。

答案:

// 時(shí)間復(fù)雜度 O(N)钝荡,空間復(fù)雜度 O(1)
int findDuplicate(int array[], int length)
{
    if (nullptr == array || length <= 0)
        return -1;

    for (int i=0; i<length; ++i)
    {
        if (array[i]<0 || array[i]>length-1)
            return -1;
    }

    for (int i=0; i<length; ++i)
    {
        while (array[i] != i)
        {
            if (array[i] == array[array[i]])
            {
                return array[i];
            }
            else
            {
                int temp = array[i];
                array[i] = array[array[i]];
                array[temp] = temp;
            }
        }
    }

    return -1;
}


題目2:不修改數(shù)組的情況下找出重復(fù)的數(shù)字。在一個(gè)長(zhǎng)度為N+1的數(shù)組里舶衬,所有的數(shù)字都在[1,N]的范圍內(nèi)埠通。請(qǐng)找出數(shù)組中任意一個(gè)重復(fù)的數(shù)字,但不能修改輸入的數(shù)組逛犹。

解析:上面的那個(gè)算法會(huì)修改原有的數(shù)組端辱。如果要求不修改原有數(shù)組,也不能使用 O(n) 的空間來復(fù)制一份虽画,可以犧牲一點(diǎn)時(shí)間復(fù)雜度舞蔽,那就用下面這個(gè)算法。

首先我們考慮一個(gè)事實(shí):如果統(tǒng)計(jì)數(shù)組里面的數(shù)字码撰,在區(qū)間 [1,N] 的范圍內(nèi)如果出現(xiàn)超過了N個(gè)數(shù)渗柿,那就說明一定至少有一個(gè)數(shù)字重復(fù)了。就像一年有 365 天 (不考慮平閏年)脖岛,那么 366 個(gè)人中一定至少有兩個(gè)人的生日重復(fù)了朵栖。

我們可以使用這個(gè)方法來二分的統(tǒng)計(jì)數(shù)字的出現(xiàn)個(gè)數(shù)。題目說所有的數(shù)字都在 [1,N] 的范圍內(nèi)柴梆,那么我就統(tǒng)計(jì) [1,N/2] 和 [N/2+1,N] 的數(shù)字出現(xiàn)次數(shù)陨溅,哪一個(gè)次數(shù)超過 N/2,哪一個(gè)就一定有重復(fù)的數(shù)字轩性。接下來在重復(fù)的那個(gè)區(qū)間內(nèi)繼續(xù)二分統(tǒng)計(jì)声登,直到區(qū)間退化為一個(gè)數(shù)字狠鸳,而且這個(gè)數(shù)字出現(xiàn)的次數(shù)大于1,那么就找到了這個(gè)數(shù)字悯嗓。每統(tǒng)計(jì)一次出現(xiàn)次數(shù)會(huì)占用 O(N) 的時(shí)間件舵。二分的統(tǒng)計(jì)最多有 log(N) 次統(tǒng)計(jì)。所以時(shí)間復(fù)雜度為 O(NlogN)脯厨。

舉個(gè)例子铅祸。{1,2,3,3,4,5,6,7},長(zhǎng)度為 8合武,每個(gè)數(shù)字都在 [1,7] 的范圍內(nèi)临梗。先統(tǒng)計(jì) [1,3] 的,有四次稼跳,[4,7] 的盟庞,也有四次。那么繼續(xù)統(tǒng)計(jì) [1,3]汤善。1 出現(xiàn)了一次什猖,[2,3] 出現(xiàn)了三次。繼續(xù)統(tǒng)計(jì) [2,3]红淡。2 出現(xiàn)了一次不狮,3 出現(xiàn)了兩次,BOOM在旱,返回 3摇零。

答案:

//時(shí)間復(fù)雜度為 O(NlogN),空間復(fù)雜度為 O(1)
int count(const int array[], int length, int beg, int end)
{
    if (nullptr==array || length<=0)
        return -1;

    int cnt = 0;
    for (int i=0; i<length; ++i)
    {
        if (array[i]>=beg && array[i]<=end)
            ++cnt;
    }
    return cnt;
}

int findDuplicate(const int array[], int length)
{
    if (nullptr==array || length<=0)
        return -1;
    for (int i=0; i<length; ++i)
    {
        if (array[i]<1 || array[i]>length)
        {
            return -1;
        }
    }

    int beg = 1, end = length-1;
    while (beg<=end)
    {
        int mid = ((end-beg)>>1) + beg;
        int cnt = count(array, length, beg, mid);
        if (beg==end)
        {
            if (cnt>1) return beg;
            else return -1;
        }

        if (cnt>(mid-beg+1)) end = mid;
        else beg = mid+1;
    }

    return -1;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末桶蝎,一起剝皮案震驚了整個(gè)濱河市驻仅,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌俊嗽,老刑警劉巖雾家,帶你破解...
    沈念sama閱讀 210,914評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件铃彰,死亡現(xiàn)場(chǎng)離奇詭異绍豁,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)牙捉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評(píng)論 2 383
  • 文/潘曉璐 我一進(jìn)店門竹揍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人邪铲,你說我怎么就攤上這事芬位。” “怎么了带到?”我有些...
    開封第一講書人閱讀 156,531評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵昧碉,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng)被饿,這世上最難降的妖魔是什么四康? 我笑而不...
    開封第一講書人閱讀 56,309評(píng)論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮狭握,結(jié)果婚禮上闪金,老公的妹妹穿的比我還像新娘。我一直安慰自己论颅,他們只是感情好哎垦,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,381評(píng)論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著恃疯,像睡著了一般漏设。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上今妄,一...
    開封第一講書人閱讀 49,730評(píng)論 1 289
  • 那天愿题,我揣著相機(jī)與錄音,去河邊找鬼蛙奖。 笑死潘酗,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的雁仲。 我是一名探鬼主播仔夺,決...
    沈念sama閱讀 38,882評(píng)論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼攒砖!你這毒婦竟也來了缸兔?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,643評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤吹艇,失蹤者是張志新(化名)和其女友劉穎惰蜜,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體受神,經(jīng)...
    沈念sama閱讀 44,095評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡抛猖,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,448評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了鼻听。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片财著。...
    茶點(diǎn)故事閱讀 38,566評(píng)論 1 339
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖撑碴,靈堂內(nèi)的尸體忽然破棺而出撑教,到底是詐尸還是另有隱情,我是刑警寧澤醉拓,帶...
    沈念sama閱讀 34,253評(píng)論 4 328
  • 正文 年R本政府宣布伟姐,位于F島的核電站收苏,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏愤兵。R本人自食惡果不足惜倒戏,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,829評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望恐似。 院中可真熱鬧杜跷,春花似錦、人聲如沸矫夷。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽双藕。三九已至淑趾,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間忧陪,已是汗流浹背扣泊。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評(píng)論 1 264
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留嘶摊,地道東北人延蟹。 一個(gè)月前我還...
    沈念sama閱讀 46,248評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像叶堆,于是被迫代替她去往敵國(guó)和親阱飘。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,440評(píng)論 2 348

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

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理虱颗,服務(wù)發(fā)現(xiàn)沥匈,斷路器,智...
    卡卡羅2017閱讀 134,626評(píng)論 18 139
  • 描述: 在一個(gè)長(zhǎng)度為 n 的數(shù)組里的所有數(shù)字都在 0 到 n-1 的范圍內(nèi)忘渔。數(shù)組中某些數(shù)字是重復(fù)的高帖,但不知道有幾個(gè)...
    要記錄的Ivan閱讀 221評(píng)論 0 0
  • 哭聲,生命降臨畦粮。你散址,從此又多了一個(gè) 身份,我的奶奶锈玉。 你說爪飘,一歲的時(shí)候就和你睡在一起了义起。 柔嫩的小...
    沒名字了是吧閱讀 328評(píng)論 0 1
  • 打開電視 隨意調(diào)電視頻道 突然間換到了 央視的《朗讀者》的節(jié)目 還好節(jié)目還未開始 節(jié)目開始 那個(gè)我們熟悉的央...
    Joe_太君閱讀 244評(píng)論 0 1
  • 最近廣州陰雨連綿拉背,斬不斷的愁緒,硬是要人的情緒也一帶感染默终。我最不喜的椅棺,是每逢這個(gè)時(shí)候犁罩,走路就得小心翼翼,生怕一不小...
    pinnocchio閱讀 200評(píng)論 0 0