二分指在有序的陣列中快速查詢某個值,這裡不得不提到我們生活中經常遇到的乙個小遊戲:猜數字,給定乙個範圍,讓你猜某個數字,每次猜錯就告訴你是大了還是小了。
按照二分的思想,猜數字時我們會直接猜中間的數字,比如給出範圍1~1000,我們會直接猜500,再看是大了還是小了再折半縮小需要猜的範圍,這樣就可以用最少的次數猜到數字。
例如我們要在1~1000的範圍內猜799,注意:預設下取整
猜(1 + 1000) / 2 = 500:小了
猜(500 + 1000) / 2 = 750:小了
猜(750 + 1000) / 2 = 875:大了
猜(750 + 875) / 2 = 812:大了
猜(750 + 812) / 2 = 781:小了
猜(781 + 812) / 2 = 796:小了
猜(796 + 812) / 2 = 804:大了
猜(796 + 804) / 2 = 800:大了
猜(796 + 800) / 2 = 798:小了
猜(798 + 800) / 2 = 799:完畢
雖然例子比較長,但應該能換來清晰直觀的理解。
可以發現每次都是(左端點 + 右端點) / 2
,再看是大了還是小了去限制左端點和右端點,如果是大了就縮小右端點的值,如果是小了就擴大左端點的值。
c++自帶的lower_bound
與upper_bound
這兩個函式,可以幫我們用二分在陣列中快速查詢某個值。
注意:陣列必須是有序的(這兩個函式預設陣列是從小到大排序的),如果要在無序陣列中查詢則需要先排序,可以看看 資料結構與演算法入門指南 - 排序
lower_bound(begin, end, x)
返回在(begin, end-1)區間中第乙個大於等於x的值的位址。
upper_bound(begin, end, x)
返回在(begin, end-1)區間中第乙個大於x的值的位址。
可以看到lower_bound
與upper_bound
的區別就是乙個可以等於x,可以只能大於x。
如果x不在陣列中,這兩個函式會返回什麼呢?
那麼我們來看看實際用法吧
int a[10]
=;int index =
lower_bound
(a, a +10,
9)- a;
//獲取符合值的下標 用返回元素的位址減去首位址就是下標了
int value = a[index]
;//獲取值 也可以一步到位 value = *lower_bound(a, a + 10, 9)
如何判斷x是否在陣列內呢?
int a[10]
=;int index =
lower_bound
(a, a +10,
11)- a;
//獲取符合值的下標
//判斷下標是否出界和值是否相同即可
if(index <
10&& a[index]
==11
)puts
("11在a中");
else
puts
("11不在a中"
);
如果陣列是從大到小排序的呢?就不能用lower_bound
與upper_bound
了嘛?當然不是,這兩個方法還有第四個引數,傳入方法或匿名函式,可以自定義排序。
int a[10]
=;int value =
*lower_bound
(a, a +10,
66, greater<
int>()
);//value = 66
有些可以用二分法來快速查詢值的情況,但無法直接用lower_bound
與upper_bound
怎麼辦呢?就例如結構體陣列中要查詢某個元素的屬性符合該值,那麼我們就只能手寫二分了。
由於邊界問題會導致死迴圈,所以某些情況下需要+1
或-1
。
bool
check
(int x)
// 檢查x是否滿足某種性質
// 區間[l, r]被劃分成[l, mid]和[mid + 1, r]時使用
intbsearch
(int l,
int r)
return l;
}// 區間[l, r]被劃分成[l, mid - 1]和[mid, r]時使用
intbsearch
(int l,
int r)
return l;
}
看個小栗子?,其實就是把lower_bound
實現了,動動手,看能不能寫出upper_bound
的效果(查詢大於x的第乙個值)?
int a[10]
=;int x =35;
// 要查詢的值
int l =
0, r =9;
while
(l < r)
cout << l <<
' '<< a[l]
;// 6 35
這裡需要提一下的就是,判斷兩個浮點數是否相等的方法一般是不會用==
來判斷的,會有誤差,那麼我們只需要判斷兩個浮點數差的絕對值非常小就行了。1e-6 = 0.000001
bool
check
(double x)
// 檢查x是否滿足某種性質
double
bsearch
(double l,
double r)
return l;
}
再來看個栗子?,數的三次方根。
double x =99;
//要求99的三次方根
double l =
-10e5
, r =
10e5
;//可以取大一點範圍,二分的效率很高的
while
(r - l >
10e-8
)//如果兩個浮點數不相等
cout << l;
// 4.62606
待增加,還是放上洛谷的官方題單。
【演算法1-6】二分查詢與二分答案 - 題單 - 洛谷 | 電腦科學教育新生態 (luogu.com.cn)
資料結構與演算法 二分查詢
二分查詢的思想是在已經排序 公升序 的陣列中,如果要查詢的數比中位數小,那麼其位置只可能在左半部分,相反只能在右半部分。這樣每次把查詢區間縮小一半,比順序查詢效率快得多。非遞迴寫法 public static int binarysearchinasclooply int nums,int star...
資料結構與演算法,二分查詢
1.時間複雜度 每次能去掉一半即 logn 2.實現方式 while迴圈 與 遞迴 我更推薦 while 迴圈,因為遞迴有個潛在的問題就是 stack over flow 堆疊溢位 而且在實際工程中是盡量避免遞迴的。雖然遞迴寫起來方便,也不容易出錯。3.實現關鍵點 我總結了下,一共有以下四點 sta...
資料結構與演算法 二分查詢
基礎概念 二分查詢又稱折半查詢,它是一種效率較高的查詢方法。二分查詢要求 線性表是有序表,即表中結點按關鍵字有序,並且要用陣列作為表的儲存結構。不妨設有序表是遞增有序的。通俗理解 每次首先找到陣列的中間位置 middle 然後把待查詢數 target 與middle進行比較。如果查詢數target ...