程序設計與算法(一) 第十一周

二分查找

程序或算法的時間復雜度

  • 一個程序或算法的時間效率,也稱“時間復雜度”,有時簡稱“復雜度”
  • 復雜度常用大的字母O和小寫字母n來表示,比如O(n),O(n2)等。n代表問題的
    規模
  • 時間復雜度是用算法運行過程中,某種時間固定的操作需要被執行的次數和n
    的關系來度量的。在無序數列中查找某個數,復雜度是O(n)
  • 計算復雜度的時候,只統計執行次數最多的(n足夠大時)那種固定操作的次數
    。比如某個算法需要執行加法n2次,除法n次,那么就記其復雜度是O(n2)的。

插入排序

void InsertionSort(int a[] ,int size)
{
for(int i = 1;i < size; ++i ) {
//a[i]是最左的無序元素,每次循環將a[i]放到合適位置
for(int j = 0; j < i; ++j)
if( a[j]>a[i]) {
//要把a[i]放到位置j,原下標j到 i-1的元素都往后移一個位子
int tmp = a[i];
for(int k = i; k > j; --k)
a[k] = a[k-1];
a[j] = tmp;
break;
}
}
} //復雜度O(n2)
  • 如果復雜度是多個n的函數之和,則只關心隨n的增長增長得最快的那個函數
    O(n3+n2
    ) => O(n3
    )
    O(2n+n3
    ) => O(2n
    )
    O(n! + 3n
    ) => O(n!)

  • 常數復雜度:O(1) 時間(操作次數)和問題的規模無關

  • 對數復雜度:O(log(n))

  • 線性復雜度:O(n)

  • 多項式復雜度:O(n
    k )

  • 指數復雜度:O(an )

  • 階乘復雜度:O(n! )

  • 復雜度有“平均復雜度”和“最壞復雜度”兩種。
    兩者可能一致,也可能不一致

  • 在無序數列中查找某個數(順序查找) O(n)

  • 平面上有n個點,要求出任意兩點之間的距離 O(n2)

  • 插入排序、選擇排序、冒泡排序 O(n2)

  • 快速排序 O( n*log(n))

  • 二分查找 O(log(n))

二分查找

  • A心里想一個1-1000之間的數,B來猜,可以問問題,A只能回答是或否。
    怎么猜才能問的問題次數最少?是1嗎?是2嗎?.......是999嗎? 平均要問500次大于500嗎?大于750嗎?大于625嗎? ......每次縮小猜測范圍到上次的一半,只需要 10次

二分查找函數

  • 寫一個函數BinarySeach,在包含size個元素的、從小到大排序的int數組a里查找元素p,如果找到,則返回元素下標,如果找不到,則返回-1。要求復雜度O(log(n))
int BinarySearch(int a[],int size,int p)
{
int L = 0; //查找區間的左端點
int R = size - 1; //查找區間的右端點
while( L <= R) { //如果查找區間不為空就繼續查找
int mid = L+(R-L)/2; //取查找區間正中元素的下標
if( p == a[mid] )
return mid;
else if( p > a[mid])
L = mid + 1; //設置新的查找區間的左端點
else
R = mid - 1; //設置新的查找區間的右端點
}
return -1;
} //復雜度O(log(n))
  • 寫一個函數LowerBound,在包含size個元素的、從小到大排序的int數組a里查找比給定整數p小的,下標最大的元素。找到則返回其下標,找不到則返回-1
int LowerBound(int a[],int size,int p) //復雜度O(log(n))
{
int L = 0; //查找區間的左端點
int R = size - 1; //查找區間的右端點
int lastPos = -1; //到目前為止找到的最優解
while( L <= R) { //如果查找區間不為空就繼續查找
int mid = L+(R-L)/2; //取查找區間正中元素的下標
if(a[mid]>= p)
R = mid - 1;
else {
lastPos = mid;
L = mid+1;
}
}
return lastPos;
}
  • 注意:
    int mid = (L+R)/2; //取查找區間正中元素的下標
  • 為了防止 (L+R)過大溢出:
    int mid = L+(R-L)/2;

二分法求方程的根
求下面方程的一個根:f(x) = x3-5x2+10^x-80 = 0
若求出的根是a,則要求 |f(a)| <= 10^(-6)

  • 解法:對f(x)求導,得f'(x)=3x^2-10x+10。由一元二次方程求根公式知方程
    f'(x)= 0 無解,因此f'(x)恒大于0。故f(x)是單調遞增的。易知 f(0) < 0且
    f(100)>0,所以區間[0,100]內必然有且只有一個根。由于f(x)在[0,100]內是
    單調的,所以可以用二分的辦法在區間[0,100]中尋找根。

二分法求方程的根

#include <cstdio>
#include <iostream>
#include <cmath>
using namespace std;
double EPS = 1e-6;
double f(double x) { return x*x*x - 5*x*x + 10*x - 80; }
int main() {
double root, x1 = 0, x2 = 100,y;
root = x1+(x2-x1)/2;
int triedTimes = 1; //記錄一共嘗試多少次,對求根來說不是必須的
y = f(root);
while( fabs(y) > EPS) {
if( y > 0 ) x2 = root;
else x1 = root;
root = x1+(x2 - x1)/2;
y = f(root);
triedTimes ++;
}
printf("%.8f\n",root);
printf("%d",triedTimes);
return 0;
}

例題1
輸入n ( n<= 100,000)個整數,找出其中的兩個數,它們之和等于整數m(假定
肯定有解)。題中所有整數都能用 int 表示
解法1:用兩重循環,枚舉所有的取數方法,復雜度是O(n2)的。
for(int i = 0;i < n-1; ++i)
for(int j = i + 1; j < n; ++j)
if( a[i]+a[j] == m)
break;
100,0002 = 100億,在各種OJ上提交或參加各種程序設計競賽,這樣的復雜度都會超時

解法2:

  1. 將數組排序,復雜度是O(n×log(n))
  2. 對數組中的每個元素a[i],在數組中二分查找m-a[i],看能否找到。復雜度log(n)
    ,最壞要查找n-2次,所以查找這部分的復雜度也是O(n×log(n))
    這種解法總的復雜度是O(n×log(n))的

解法3:

  1. 將數組排序,復雜度是O(n×log(n))
  2. 查找的時候,設置兩個變量i和j,i初值是0,j初值是n-1.看a[i]+a[j],如果大于m,
    就讓j減1,如果小于m,就讓i加1,直至a[i]+a[j]=m。
    這種解法總的復雜度是O(n×log(n))的。

例題2 百練 2456:Aggressive cows
http://bailian.openjudge.cn/practice/2456
農夫 John 建造了一座很長的畜欄,它包括N (2≤N≤100,000)個隔間,這
些小隔間的位置為x0
,...,xN-1 (0≤xi≤1,000,000,000,均為整數,各不相同).
John的C (2≤C≤N)頭牛每頭分到一個隔間。牛都希望互相離得遠點省得
互相打擾。怎樣才能使任意兩頭牛之間的最小距離盡可能的大,這個最
大的最小距離是多少呢?

  • 解法1:

先得到排序后的隔間坐標 x0,...,xN-1

從1,000,000,000/C到1依次嘗試這個“最大的最近距離”D, 找到的
第一個可行的就是答案。

嘗試方法:

  1. 第1頭牛放在x0
  2. 若第k頭牛放在xi ,則找到xi+1到xN-1中第一個位于[xi+D, 1,000,000,000]中的Xj
    第k+1頭牛放在Xj。找不到這樣的Xj,則 D=D-1,轉 1)再試

若所有牛都能放下,則D即答案
復雜度 1,000,000,000/C *N,即 1,000,000,000, 超時!

  • 解法2:

先得到排序后的隔間坐標 x0,...,xN-1

在[L,R]內用二分法嘗試“最大最近距離”D = (L+R)/2 (L,R初值為
[1, 1,000,000,000/C]

若D可行,則記住該D,然后在新[L,R]中繼續嘗試(L= D+1)
若D不可行,則在新[L,R]中繼續嘗試(R= D-1)

復雜度 log(1,000,000,000/C) * N

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,431評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,637評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,555評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,900評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,629評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,976評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,976評論 3 448
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,139評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,686評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,411評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,641評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,129評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,820評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,233評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,567評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,362評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,604評論 2 380

推薦閱讀更多精彩內容