閑話二分查找(搜索)

二分查找(搜索)的英文名是 Binary Search,直譯過來其實(shí)應(yīng)該叫二進(jìn)制搜索,但這些都不重要,只要知道有這么個(gè)英文名就行。

什么是二分查找

二分查找是一種在每次比較之后將查找空間一分為二的算法。每次需要查找集合中的索引或元素時(shí),都應(yīng)該考慮二分查找。如果集合是無序的,我們需要在應(yīng)用二分查找之前先對其進(jìn)行排序。
二分查找是計(jì)算機(jī)科學(xué)中最基本、最有用的算法之一。 它描述了在有序集合中搜索特定值的過程。二分查找中使用的術(shù)語有:

  • 目標(biāo) Target —— 你要查找的值
  • 索引 Index —— 你要查找的當(dāng)前位置
  • 左、右指示符 Left,Right —— 我們用來維持查找空間的指標(biāo)
  • 中間指示符 Mid —— 我們用來應(yīng)用條件來確定我們應(yīng)該向左查找還是向右查找的索引

二分的本質(zhì)

我們平時(shí)所說的二分大多指數(shù)組的二分,因?yàn)閿?shù)組可以隨機(jī)訪問嘛;
不過這種二分實(shí)在太狹義了,二分的本質(zhì)是將問題規(guī)??s小到一半,因此二分和數(shù)據(jù)結(jié)構(gòu)沒有本質(zhì)關(guān)系!
但不同的數(shù)據(jù)結(jié)構(gòu)卻給二分賦予了不同的色彩;
比如:

跳表就是鏈表的二分(比如redis的跳躍表);
二叉搜索樹就是樹的二分;
……;

二分查找的三個(gè)基本組成部分

二分查找的先決條件是【有序的折半邏輯】,即每次折半查詢時(shí),有條件區(qū)分出另一半數(shù)據(jù),不一定非得是數(shù)學(xué)上的升序降序;二分查找一般由三個(gè)主要部分組成:

  • 預(yù)處理 —— 如果集合未排序,則進(jìn)行排序。
  • 二分查找 —— 使用循環(huán)或遞歸在每次比較后將查找空間劃分為兩半。
  • 后處理 —— 在剩余空間中確定可行的候選者。

二分查找的三種基本模板

模板-I-1
  • 一般用于精確查找值;
  • 結(jié)束條件:L>R;
while ($L <= $R) {
    $mid=$L+floor(($R-$L)/2);//防止溢出
    if ($mid < $target) {
        $L = $mid+1;
    } else if ($mid > $target) {
        $R = $mid-1;
    } else if ($mid == $target) {
        return $mid; //注意,此模板一般直接返回結(jié)果
    } 
}
return null;
模板-II-2
  • 一般用于尋找左邊界;
  • 結(jié)束條件:L==R;
while ($L < $R) {
    $mid=$L+floor(($R-$L)/2);//防止溢出
    if ($mid < $target) {
        $L = $mid+1;
    } else if ($mid >= $target) {
        $R = $mid; //注意此處R=mid
    }
}
return $R;//L或R都行,因?yàn)榻Y(jié)束條件是L==R;
模板-III
int binarySearch(int[] nums, int target) {
    int left = 0, right = nums.length - 1;
    while (left + 1 < right){
        // Prevent (left + right) overflow
        int mid = left + (right - left) / 2;
        if (nums[mid] == target) {
            return mid;
        } else if (nums[mid] < target) {
            left = mid;
        } else {
            right = mid;
        }
    }
    // Post-processing:
    // End Condition: left + 1 == right
    if(nums[left] == target) return left;
    if(nums[right] == target) return right;
    return -1;
}

模板 #3 是二分查找的另一種獨(dú)特形式:
它用于搜索需要訪問當(dāng)前索引及其在數(shù)組中的直接左右鄰居索引的元素或條件。

模板-III 的邊界條件
  • 初始條件:left = 0, right = length-1
  • 終止:left + 1 == right
  • 向左查找:right = mid
  • 向右查找:left = mid

透過小白兔(鼠)試毒問題去看二分的本質(zhì)

盡管上面已經(jīng)說了, 不要將二分法局限于某種數(shù)據(jù)結(jié)構(gòu), 但多數(shù)人還是習(xí)慣于狹義的將二分法理解為數(shù)組的二分, 總覺著問題得能具象化為一組數(shù)才可以施展二分大法。
實(shí)則不然, 上面提到過, 二分的本質(zhì)在于將問題規(guī)??s小到原來的一半, 所以請看下面這道題:

現(xiàn)在有1000瓶藥水, 其中1瓶是毒藥, 只需一滴就可讓小白兔在24小時(shí)內(nèi)死亡; 問,如何在最短時(shí)間內(nèi)用最少的小白兔, 找出這瓶毒藥?

該題據(jù)說是某大(鵝)廠面試題, 看上去似乎挺簡單:
1000只小兔子, 挨個(gè)兒試; 或者, 1只兔子, 一天試一瓶;

不過嘛, 想想也知道答案不是這樣, 要不, 就沒有考你的必要了。
那么,如何巧妙的找到這瓶毒藥呢?
這就要用到本文一直在講的二分法了;乍一看,這題貌似和二分扯不上關(guān)系,如果我們把題目簡化一下,就好理解了。

現(xiàn)在有4瓶藥水, 其中1瓶是毒藥, 只需一滴就可讓小白兔在24小時(shí)內(nèi)死亡; 問,如何在最短時(shí)間內(nèi)用最少的小白兔, 找出這瓶毒藥?

先套用上面的簡單解法:4只小兔子,或者1只試4天;
發(fā)散你的小思維再想一想,能否再縮短一些?
實(shí)際上,這個(gè)簡單方案,用不到4只或4天,只需3只或3天即可,因?yàn)樽詈笠黄靠梢杂门懦ǖ玫酱鸢福?br> 所以,問題就可以繼續(xù)簡化為:

現(xiàn)在有3瓶藥水, 毒藥可能在里邊也可能不在,如何用最小的代價(jià)確定它在不在里邊。

接下來該如何優(yōu)化這個(gè)解法呢,也就是,如何把3瓶不相干的藥水二分呢?
如果你沒有思路,就想想二分法的英文名,它為什么叫 Binary Search?
其實(shí)就是將數(shù)字轉(zhuǎn)化為二進(jìn)制吖!聰明的你是不是也想到了呢?

現(xiàn)在我們將 1到3號瓶 按二進(jìn)制進(jìn)行編號:

01 - 1號瓶
10 - 2號瓶
11 - 3號瓶

它們的共性是什么,也就一目了然了,每一個(gè)數(shù)都由兩位二進(jìn)制數(shù)構(gòu)成,如果我們把問題分成這么兩類來看:

1、有毒藥水的第1位是否為1;
2、有毒藥水的第2位是否為1;

是不是就可以借用二分思路了呢?

根據(jù)這個(gè)思路,需要請來兩只小兔子奉獻(xiàn)一下,將其編號為1號兔和2號兔,并將3瓶藥水按上面的二進(jìn)制表達(dá)式編號:

1號兔只喂二進(jìn)制第一位是1的藥水;
2號兔只喂二進(jìn)制第二位是1的藥水;

24小時(shí)后,如果:
1號兔死亡,說明有毒的是3瓶中的第1瓶;
2號兔死亡,說明有毒的是3瓶中的第2瓶;

如果1號和2號都沒事兒,說明有毒的是被我們拋棄的那一瓶;為了便于統(tǒng)一觀察,可以將被拋棄的那瓶藥水的二進(jìn)制編號為 00,轉(zhuǎn)換為十進(jìn)制即第0號瓶:

01 - 1號瓶
10 - 2號瓶
11 - 3號瓶
00 - 0號(4號)瓶

如此二分優(yōu)化下來,4瓶藥水,我們只需2只小兔子奉獻(xiàn),就可在1天內(nèi)找出有毒藥水;

下面我們把瓶子數(shù)量:
擴(kuò)展到100瓶;套用二進(jìn)制思路,只需7只就可在1天內(nèi)找出有毒藥水;
再擴(kuò)展到1000瓶,只需10只就可在1天內(nèi)找出有毒藥水;

聰明的你想到答案了嗎?看完這道題,你是否對二分法有了更深入的了解呢?



提示:
99的二進(jìn)制表達(dá)式為:1100011;
999的二進(jìn)制表達(dá)式為:1111100111;
嚴(yán)格來說,原題中的1000瓶解題思路并非是二分法,稱之為十分法更為貼切(100瓶則應(yīng)稱之為七分法),但本質(zhì)都是套用了二分法的思路,將問題規(guī)模縮小到原來的十分之一;

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容