深入談?wù)劧植檎易冃蔚碾y點(diǎn)

昨天我們簡(jiǎn)短地談了談二分查找的變形看铆,其實(shí)都是很簡(jiǎn)單的轉(zhuǎn)換挤巡,不費(fèi)力唠帝,主要是為了拋磚引玉,讓大家明白二分查找的題目的特點(diǎn)玄柏,從而引出今天的討論:會(huì)給一個(gè)排序好的數(shù)組襟衰,然后在這之中去尋找符合條件的元素。

事情起源于我前些日子面試遇到的算法題(現(xiàn)在開發(fā)面試遇到的算法題真是越來(lái)越多了粪摘,開發(fā)框架可以來(lái)了學(xué)瀑晒,但算法一定要強(qiáng)的架勢(shì),大家平時(shí)有空的話還是做做題玩玩)徘意,題目是這樣的:
假設(shè)按照升序排序的數(shù)組在預(yù)先未知的某個(gè)點(diǎn)上進(jìn)行了旋轉(zhuǎn)苔悦。( 例如,數(shù)組 [0,1,2,4,5,6,7] 可能變?yōu)?[4,5,6,7,0,1,2] )椎咧。搜索一個(gè)給定的目標(biāo)值玖详,如果數(shù)組中存在這個(gè)目標(biāo)值,則返回它的索引勤讽,否則返回 -1 蟋座。你可以假設(shè)數(shù)組中不存在重復(fù)的元素。你的算法時(shí)間復(fù)雜度必須是 O(log n) 級(jí)別脚牍。

快速掃一下題意向臀,我們已經(jīng)鎖定排序好找出這兩個(gè)關(guān)鍵字了诸狭,再加上復(fù)雜度要求在O(log n)級(jí)別券膀,暗示得很明顯了君纫,盡管它花里胡哨地來(lái)了個(gè)旋轉(zhuǎn),我的直覺也告訴我先嘗試往二分查找上套芹彬。

來(lái)吧蓄髓,那我們就來(lái)看看,怎樣才能讓題目回到我們熟悉的二分查找系列舒帮。主要的障礙在于它排序好之后又進(jìn)行了旋轉(zhuǎn)会喝,讓本來(lái)的排序不那么直觀了。我們得想辦法会前,如果有序了我們想找某個(gè)元素就很簡(jiǎn)單了好乐。這時(shí)候我們可以發(fā)現(xiàn)匾竿,總存在一個(gè)點(diǎn)瓦宜,讓旋轉(zhuǎn)后的數(shù)組在那一點(diǎn)前后兩段還是有序的!那我們隨便從中間切一半岭妖,總有一半是完全遞增的临庇。

我們先計(jì)算出中值middle,然后我們比較一下在start跟在middle的兩個(gè)點(diǎn)昵慌,我們有兩個(gè)選擇:

  1. arr[start] <= arr[middle]假夺,那這部分是遞增的。
  2. 不然的話后半部分是遞增的斋攀。

一旦我們知道哪部分是排序好的已卷,我們就可以縮短范圍了:

  1. 用目標(biāo)值比較arr[start]跟[middle],我們就可以判斷目標(biāo)值在不在這部分里面淳蔼,如果在的話就可以丟棄第二部分了侧蘸。
  2. 否則的話我們丟棄第一部分,在第二部分里面去找鹉梨。

反正數(shù)組里沒有重復(fù)讳癌,我們每次都可以丟掉一半,如果數(shù)組里面有重復(fù)這邊判斷就要復(fù)雜一些了存皂,我們稍后再看有重復(fù)的版本晌坤,現(xiàn)在先繼續(xù)看。只要一直找到有序的部分旦袋,查找就不是難事骤菠。

public static int search(int[] arr, int key) {
        int start = 0, end = arr.length - 1;
        while (start <= end) {
            int mid = start + (end - start) / 2;
            if (arr[mid] == key)
                return mid;

            if (arr[start] <= arr[mid]) { // 左邊升序
                if (key >= arr[start] && key < arr[mid]) {
                    end = mid - 1;
                } else { //key > arr[mid]
                    start = mid + 1;
                }
            } else { // 右邊升序  
                if (key > arr[mid] && key <= arr[end]) {
                    start = mid + 1;
                } else {
                    end = mid - 1;
                }
            }
        }

        // 找不到
        return -1;
    }

看吧,一開始的直覺果然是對(duì)的疤孕,這題目最后還是我們熟悉的二分查找娩怎,時(shí)間復(fù)雜度也維持在了O(log n)。這也是我之前強(qiáng)調(diào)的胰柑,大家刷題的時(shí)候多按類型來(lái)做截亦,總結(jié)出題目的規(guī)律爬泥,萬(wàn)變不離其宗,雖然我們不可能把所有的題目都做完崩瓤,但是我們會(huì)找規(guī)律啊袍啡。就像排序好的數(shù)組找某個(gè)元素會(huì)讓我們聯(lián)系到二分查找,在我們的記憶里就把他們形成一個(gè)強(qiáng)關(guān)聯(lián)却桶,我們發(fā)現(xiàn)一種類型的套路之后境输,我們?cè)儆龅筋愃茊?wèn)題就可以給自己一個(gè)大致方向,引導(dǎo)自己往正確的路上走颖系。

現(xiàn)在再來(lái)看看剛才埋下的坑嗅剖,如果數(shù)組里有重復(fù)元素會(huì)怎么樣?上面的方法就不好使了嘁扼,如果某一時(shí)刻start信粮,middle,end的值都一樣的趁啸,我們沒辦法判斷某個(gè)部分是不是升序了强缘。

那這時(shí)候該怎么辦,不用想的太復(fù)雜不傅,我們的障礙主要來(lái)自于三個(gè)值相等旅掂,那如果start,middle访娶,end指的這個(gè)值不是我們要找的商虐,那我們直接跳過(guò)就好了,跳過(guò)他們等這三個(gè)位置值不想等了崖疤,我們的思路不就又跟之前一樣了嗎秘车?

public static int search(int[] arr, int key) {
        int start = 0, end = arr.length - 1;
        while (start <= end) {
            int mid = start + (end - start) / 2;
            if (arr[mid] == key)
                return mid;

            // 跟上邊解法區(qū)別就在這個(gè)判斷,可能在start戳晌,middle鲫尊,end三個(gè)位置值相等,這時(shí)候我們不知道選哪邊
            // 我們可以選擇兩邊都跳過(guò)沦偎,如果key != arr[mid]的話
            if ((arr[start] == arr[mid]) && (arr[end] == arr[mid])) {
                ++start;
                --end;
            } else if (arr[start] <= arr[mid]) { // 左邊升序
                if (key >= arr[start] && key < arr[mid]) {
                    end = mid - 1;
                } else {
                    start = mid + 1;
                }
            } else { // 右邊升序
                if (key > arr[mid] && key <= arr[end]) {
                    start = mid + 1;
                } else {
                    end = mid - 1;
                }
            }
        }

        // 找不到
        return -1;
    }

好啦疫向,最后總結(jié)一下,大家應(yīng)該都發(fā)現(xiàn)了二分查找題目想要變復(fù)雜基本上都在排序好的數(shù)組上做文章豪嚎,而且一般還會(huì)強(qiáng)調(diào)是一個(gè)排序好的數(shù)組做了什么什么轉(zhuǎn)換搔驼。相信大家以后看到這類題目都知道該怎么思考了,Happy coding~

關(guān)注我侈询,一起學(xué)算法吧
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末舌涨,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子扔字,更是在濱河造成了極大的恐慌囊嘉,老刑警劉巖温技,帶你破解...
    沈念sama閱讀 219,039評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異扭粱,居然都是意外死亡舵鳞,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門琢蛤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)蜓堕,“玉大人,你說(shuō)我怎么就攤上這事博其√撞牛” “怎么了?”我有些...
    開封第一講書人閱讀 165,417評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵慕淡,是天一觀的道長(zhǎng)背伴。 經(jīng)常有香客問(wèn)我,道長(zhǎng)儡率,這世上最難降的妖魔是什么挂据? 我笑而不...
    開封第一講書人閱讀 58,868評(píng)論 1 295
  • 正文 為了忘掉前任以清,我火速辦了婚禮儿普,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘掷倔。我一直安慰自己眉孩,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評(píng)論 6 392
  • 文/花漫 我一把揭開白布勒葱。 她就那樣靜靜地躺著浪汪,像睡著了一般。 火紅的嫁衣襯著肌膚如雪凛虽。 梳的紋絲不亂的頭發(fā)上死遭,一...
    開封第一講書人閱讀 51,692評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音凯旋,去河邊找鬼呀潭。 笑死,一個(gè)胖子當(dāng)著我的面吹牛至非,可吹牛的內(nèi)容都是我干的钠署。 我是一名探鬼主播,決...
    沈念sama閱讀 40,416評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼荒椭,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼谐鼎!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起趣惠,我...
    開封第一講書人閱讀 39,326評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤狸棍,失蹤者是張志新(化名)和其女友劉穎身害,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體草戈,經(jīng)...
    沈念sama閱讀 45,782評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡题造,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了猾瘸。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片界赔。...
    茶點(diǎn)故事閱讀 40,102評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖牵触,靈堂內(nèi)的尸體忽然破棺而出淮悼,到底是詐尸還是另有隱情,我是刑警寧澤揽思,帶...
    沈念sama閱讀 35,790評(píng)論 5 346
  • 正文 年R本政府宣布袜腥,位于F島的核電站,受9級(jí)特大地震影響钉汗,放射性物質(zhì)發(fā)生泄漏羹令。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評(píng)論 3 331
  • 文/蒙蒙 一损痰、第九天 我趴在偏房一處隱蔽的房頂上張望福侈。 院中可真熱鬧,春花似錦卢未、人聲如沸肪凛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)伟墙。三九已至,卻和暖如春滴铅,著一層夾襖步出監(jiān)牢的瞬間戳葵,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工汉匙, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留拱烁,地道東北人似嗤。 一個(gè)月前我還...
    沈念sama閱讀 48,332評(píng)論 3 373
  • 正文 我出身青樓宛畦,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親琢锋。 傳聞我的和親對(duì)象是個(gè)殘疾皇子绎秒,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評(píng)論 2 355