【leetcode邊做邊學(xué)】二分查找應(yīng)用

二分查找

二分查找算法是一種在有序數(shù)組中查找某一特定元素的搜索算法。搜素過(guò)程從數(shù)組的中間元素開(kāi)始哟楷,如果中間元素正好是要查找的元素馍刮,則搜索過(guò)程結(jié)束簸搞;如果某一特定元素大于或者小于中間元素茉贡,則在數(shù)組大于或小于中間元素的那一半中查找,而且跟開(kāi)始一樣從中間元素開(kāi)始比較者铜。如果在某一步驟數(shù)組 為空腔丧,則代表找不到。這種搜索算法每一次比較都使搜索范圍縮小一半作烟。折半搜索每次把搜索區(qū)域減少一半愉粤,時(shí)間復(fù)雜度為Ο(logn)。
二分查找的優(yōu)點(diǎn)是比較次數(shù)少拿撩,查找速度快衣厘,平均性能好;其缺點(diǎn)是要求待查表為有序表压恒,且插入刪除困難影暴。因此,二分查找探赫,是通過(guò)不斷縮小解可能存在的范圍型宙,從而求得問(wèn)題最優(yōu)解的方法,適用于不經(jīng)常變動(dòng)而查找頻繁的有序列表伦吠。

二分查找算法要求

  1. 必須采用順序存儲(chǔ)結(jié)構(gòu)
  2. 必須按關(guān)鍵字大小有序排列

二分查找算法流程圖

二分查找流程圖
二分查找流程圖

二分查找c源碼

//二分查找的遞歸版本
int binary_search_recursion(const int array[], int low, int high, int key)  
{  
    int mid = low + (high - low)/2;  
    if(low > high)  
        return -1;  
    else{  
        if(array[mid] == key)  
            return mid;  
        else if(array[mid] > key)  
            return binary_search_recursion(array, low, mid-1, key);  
        else  
            return binary_search_recursion(array, mid+1, high, key);  
    }  
}  

//二分查找的循環(huán)版本
int binary_search_loop(const int array[], int len, int key)  
{  
    int low = 0;  
    int high = len - 1;  
    int mid;  
    while(low <= high){  
        mid = (low+high) / 2;  
        if(array[mid] == key)  
            return mid;  
        else if(array[mid] > key)  
            high = mid - 1;  
        else  
            low = mid + 1;  
    }  
    return -1;  
}  

邊界錯(cuò)誤

二分查找算法的邊界一般分為兩種情況妆兑,一種為左閉右開(kāi)區(qū)間,如[low,high)毛仪,一種是左閉右閉區(qū)間搁嗓,如[low,high]。這里需要注意的是循環(huán)體外的初始化條件箱靴,與循環(huán)體內(nèi)的迭代步驟腺逛,都必須遵守一致的區(qū)間規(guī)則,也就是說(shuō)刨晴,如果循環(huán)體初始化時(shí)屉来,是以左閉右開(kāi)區(qū)間為邊界的路翻,那么循環(huán)體內(nèi)部的迭代也應(yīng)該如此。

Leetcode實(shí)例

Search in Rotated Sorted Array

要求

Suppose a sorted array is rotated at some pivot unknown to you beforehand
假設(shè)有一排好序的數(shù)組茄靠,它事先在某個(gè)軸心點(diǎn)處被旋轉(zhuǎn)
(i.e., 0 1 2 4 5 6 7 might become 4 5 6 7 0 1 2 )
You are given a target value to search.
If found in the array return its index, otherwise return -1.
You may assume no duplicate exists in the array.

分析

二分查找茂契,難度在于左右邊界的確定
如果A[middle] <= A[first],則[middle,last-1]區(qū)間的子數(shù)組為遞增序列慨绳,那它就可以用二分查找的方法去進(jìn)行查詢掉冶;否則,就會(huì)被繼續(xù)的劃分脐雪,知道子數(shù)組是一個(gè)遞增的數(shù)組為止厌小。反之也是同樣的道理。
由于last一直指向子數(shù)組最后一個(gè)元素的下一個(gè)位置战秋,所以在程序的賦值是要特別注意璧亚。

C++代碼

class Solution {
public:
    int search(int A[], int n, int target) {
        int first = 0;
        int last = n;
        while(first != last)
        {
            int middle = (first + last)/2;
            if(A[middle] == target)
                return middle;
            if(A[first] >= A[middle]){
                if(target > A[middle] && target <= A[last-1])
                    first = middle+1;
                else
                    last = middle;
            }
            else
            {
                if(target < A[middle] && target >= A[first])
                    last = middle;
                else
                    first = middle+1;
            }
        }
        return -1;
    }
};

Search in Rotated Sorted Array II

要求

Follow up for ”Search in Rotated Sorted Array”: What if duplicates are allowed?
Write a function to determine if a given target is in the array.
作為上一題的變型,該題允許存在重復(fù)的元素

分析

允許重復(fù)元素脂信,則上一題中如果 A[middle]>=A[left], 那么 [left,middle] 為遞增序列的假設(shè)就不能成立了癣蟋,比如 [1,3,1,1,1]。
如果 A[m]>=A[l] 不能確定遞增狰闪,那就把它拆分成兩個(gè)條件:

  • 若 A[m]>A[l]疯搅,則區(qū)間 [l,m] 一定遞增
  • 若 A[m]==A[l] 確定不了,那就 l++埋泵,往下看一步即可幔欧。

c++代碼

class Solution {
public:
    bool search(int A[], int n, int target) {
        int first = 0,last = n;
        while(first != last)
        {
            int mid = (first+last)/2;
            if(A[mid] == target)
                return true;
            if(A[mid] == A[first])
                first++;
            else if(A[mid] > A[first])
            {
                if(A[first]<= target && A[mid] > target)
                    last = mid;
                else
                    first = mid+1;
            }
            else
            {
                if(A[mid] < target && A[last-1] >= target)
                    first = mid+1;
                else
                    last = mid;
            }
        }
        return false;
    }
};

二分查找思想的應(yīng)用

求最優(yōu)解問(wèn)題

二分查找的方法在求最優(yōu)解的問(wèn)題上也很有用。比如“求滿足某個(gè)條件C(x)的最小的x”這一問(wèn)題丽声。對(duì)于任意滿足C(x)的x如果所有x'>=x也滿足C(x')的話礁蔗,就可以用二分查找來(lái)求得最小的x。
首先我們將區(qū)間的左端點(diǎn)初始化為不滿足C(x)的值恒序,右端點(diǎn)初始化為滿足C(x)的值瘦麸。然后每次取中點(diǎn)mid=(lb+ub)/2,判斷C(mid)是否滿足并縮小范圍,直到(lb,ub]足夠小為止歧胁。最后ub就是要求的最小值滋饲。
同理,最大化的問(wèn)題也可以用同樣的方法求解喊巍。

假定一個(gè)解并判斷是否可行

有N條繩子屠缭,他們長(zhǎng)度分別為L(zhǎng)i。如果從它們中切割出K條長(zhǎng)度相同的繩子的話崭参,這K條繩子每條最長(zhǎng)能有多長(zhǎng)呵曹?答案保留到小數(shù)點(diǎn)后2位。
限制條件

  • 1<= N <= 10000
  • 1<= K <= 10000
  • 1<= Li <= 100000

分析

令條件為C(x)=可以得到K條長(zhǎng)度為x的繩子
則問(wèn)題變?yōu)榍鬂M足C(x)條件的最大的x。在區(qū)間初始化時(shí)奄喂,令下界為lb=0铐殃,上界為ub=INF。
轉(zhuǎn)化:
由于長(zhǎng)度為L(zhǎng)i 的繩子最多可以切出floor(Li/x)段長(zhǎng)度為x的繩子跨新,因此
C(x)=(floor(Li/x)的總和是否大于或者等于K)

代碼實(shí)例

#include <iostream>
#include <math.h>
using namespace std;
#define MAX_N 10

int N = 4;
int K = 10;
double L[MAX_N] = {8.02,7.43,4.57,5.39};

bool C(double x)
{
    int num = 0;    
    for(int i=0;i<N;i++)
    {
        num += (int)(L[i]/x);
    }
    return num >= K;
}

void solve()
{
    double lb = 0;
    double ub = 1000;
    for(int i=0;i<100;i++)
    {
        double mid = (lb+ub)/2;
        if(C(mid)) lb = mid;
        else ub = mid;
    }
    cout << floor(ub*100)/100<<endl;
}

int main() {
    solve();
    return 0;
}

二分查找的結(jié)束判定

在輸出小數(shù)的問(wèn)題中富腊,一般都會(huì)制定允許的誤差范圍或者制定輸出中小數(shù)點(diǎn)后面的位數(shù)。有必要設(shè)置合理的結(jié)束條件來(lái)滿足精度的要求域帐。
在上面的程序中赘被,我們制定循環(huán)次數(shù)作為終止條件。1次循環(huán)可以吧區(qū)間范圍縮小一半肖揣,100次循環(huán)則可以達(dá)到10的-30次冪的精度范圍民假,基本是沒(méi)有問(wèn)題的。

最大化平均值

有n個(gè)物品的重量和價(jià)值分別是wi和vi龙优。從中選出k個(gè)物品使得單位重量的價(jià)值最大羊异。
限制條件:

  • 1<= k <= n <= 10^4
  • 1<= wi,vi <= 10^6

分析

最大化平均值
最大化平均值

代碼實(shí)例

//input
int n,k;
int w[MAX_N],v[MAX_N];

double y[MAX_N];// v - x * w

bool C(double x)
{
    for(int i=0;i<n;i++){
        y[i] = v[i] - x*w[i];
    }
    sort(y,y+n);
    //compute the sum of top-k number(array y)
    double sum = 0;
    for(int i=0;i<k;i++){
        sum+=y[n-i-1];
    }
    return sum >= 0;
}
void solve()
{
    double lb=0,ub=INF;
    for(int i=0;i<100;i++){
        double mid = (lb+ub)/2;
        if(C(mid))
            lb = mid;
        else
            ub = mid;
    }
    printf("%.2f\n",ub);
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市彤断,隨后出現(xiàn)的幾起案子球化,更是在濱河造成了極大的恐慌,老刑警劉巖瓦糟,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異赴蝇,居然都是意外死亡菩浙,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門句伶,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)劲蜻,“玉大人,你說(shuō)我怎么就攤上這事考余∠孺遥” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵楚堤,是天一觀的道長(zhǎng)疫蔓。 經(jīng)常有香客問(wèn)我,道長(zhǎng)身冬,這世上最難降的妖魔是什么衅胀? 我笑而不...
    開(kāi)封第一講書人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮酥筝,結(jié)果婚禮上滚躯,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好掸掏,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布茁影。 她就那樣靜靜地躺著,像睡著了一般丧凤。 火紅的嫁衣襯著肌膚如雪募闲。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 51,146評(píng)論 1 297
  • 那天息裸,我揣著相機(jī)與錄音蝇更,去河邊找鬼。 笑死呼盆,一個(gè)胖子當(dāng)著我的面吹牛年扩,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播访圃,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼厨幻,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了腿时?” 一聲冷哼從身側(cè)響起况脆,我...
    開(kāi)封第一講書人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎批糟,沒(méi)想到半個(gè)月后格了,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡徽鼎,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年盛末,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片否淤。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡悄但,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出石抡,到底是詐尸還是另有隱情檐嚣,我是刑警寧澤,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布啰扛,位于F島的核電站嚎京,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏侠讯。R本人自食惡果不足惜挖藏,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望厢漩。 院中可真熱鬧膜眠,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至辟躏,卻和暖如春谷扣,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背捎琐。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工会涎, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人瑞凑。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓末秃,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親籽御。 傳聞我的和親對(duì)象是個(gè)殘疾皇子练慕,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353

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

  • 背景 一年多以前我在知乎上答了有關(guān)LeetCode的問(wèn)題, 分享了一些自己做題目的經(jīng)驗(yàn)。 張土汪:刷leetcod...
    土汪閱讀 12,743評(píng)論 0 33
  • LeetCode 刷題隨手記 - 第一部分 前 256 題(非會(huì)員)技掏,僅算法題铃将,的吐槽 https://leetc...
    蕾娜漢默閱讀 17,764評(píng)論 2 36
  • 前言 我們先來(lái)看下二分搜索維基解釋 在計(jì)算機(jī)科學(xué)中,折半搜索(英語(yǔ):half-interval search)哑梳,也...
    偷天神貓閱讀 2,321評(píng)論 0 4
  • 數(shù)組是一種可變的劲阎、可索引的數(shù)據(jù)集合。在Scala中用Array[T]的形式來(lái)表示Java中的數(shù)組形式 T[]鸠真。 v...
    時(shí)待吾閱讀 951評(píng)論 0 0
  • 首頁(yè) 資訊 文章 資源 小組 相親 登錄 注冊(cè) 首頁(yè) 最新文章 IT 職場(chǎng) 前端 后端 移動(dòng)端 數(shù)據(jù)庫(kù) 運(yùn)維 其他...
    Helen_Cat閱讀 3,869評(píng)論 1 10