【總結】二分查詢 —— 一種減而治之的查詢方法(1)
本文為本人學習鄧俊輝教授《資料結構》一書二分查詢部分的複習總結,如需轉載請註明此書以及本文地址。
0 引言
西遊記第28回,孫悟空三打白骨精後被唐僧逐回,於是回到了花果山。
大聖道:“我當時共有四萬七千群妖,如今都往哪裡去了?”
群猴道:“自從爺爺走後,這山被二郎菩薩點上火,燒殺了大半。我們蹲在井裡,鑽在澗內,藏於鐵板橋下,得了性命。及至火滅煙消,出來時,又沒花果養贍(shan),難以存活,別處又去了一半。我們這一半,捱(ai)苦的住在山中,這兩年,又被些打獵的搶去一半也。”
老舍先生在其一部表現抗戰北平淪陷區普通民眾生活與抗戰的長篇小說《四世同堂》中描寫到:
月亮上來了。星漸漸的稀少,天上空闊起來。和微風勻到一起的光,象冰涼的刀刃兒似的,把寬靜的大街切成兩半,一半兒黑,一半兒亮。那黑的一半,使人感到陰森,亮的一半使人感到淒涼。
1 基本思想
二分查詢(Binary Search)是一種“減而治之”的策略,具體如圖1.1所示。
(圖待補充)
如果我們要在有序向量中區間的部分查詢目標元素,對於任意的元素我們可以將區間分成、、三個子區間。根據向量的有序性我們有
- 如果目標元素,則目標元素必然存在於左側的子區間或不存在,這時我們可以遞迴查詢子區間;
- 如果目標元素,則目標元素必然存在於右側的子區間或不存在,這時我們可以遞迴查詢子區間;
- 如果目標元素,則目標元素已在切分點處命中,此時查詢就可以終止了。
2 樸素演算法
2.1 策略
樸素的二分查詢演算法的切分點選自區間的中點,即。這種策略可以概括為“以當前區間內居中的元素作為目標元素的試探物件”。因為每一步迭代之後無論沿哪個方向深入新問題的規模都將縮小一半,因此從最壞的角度來看,這一策略是最優的。
2.2 實現
一種遞迴方法的具體實現如下:
//樸素的二分查詢——遞迴版
//在有序向量A的區間[lo, hi)中查詢元素額,返回e的秩
template <typename T> int binSearch(T* A, T const& e, int lo, int hi)
{
if(lo >= hi) return -1; //遞迴基或不合法,查詢失敗
int mi = (lo + hi) >> 1; //以中點mi為軸點
return (e < A[mi] ? binSearch(A, e, lo, mi) : //分情況討論
(A[mi] < e ? binSearch(A, e, mi + 1, hi) : mi);
}
通過“遞迴消除”的方法或改變思考方式,我們還可以得到以下一種迭代版本:
//樸素的二分查詢——迭代版
//在有序向量A的區間[lo, hi)中查詢元素額,返回e的秩
template <typename T> int binSearch(T* A, T const& e, int lo, int hi)
{
while(lo < hi) //如果區間[lo, hi)還有元素
{
int mi = (lo + hi) >> 1; //以中點mi為軸點
if (e < A[mi]) hi = mi; //深入[lo, mi)繼續查詢
else if (A[mi] < e) lo = mi + 1; //深入(mi, hi)繼續查詢
else return mi; //命中
}
return -1; //查詢失敗
}
我們可能會有如此疑惑:
● 三種情況(、、)為何要如此排列?如果調整為(、、)(一般書上給出的方案)會如何?
在2.5節我們重點分析這個問題,以闡明為什麼將“”的情況放在最後。
2.3 例項
(待補充)
2.4 效能
根據2.1中的策略描述,有效的查詢區間寬度按1/2的幾何級數速度遞減。因此經過至多步迭代必然終止。因為每次迭代僅需的時間,因此總體時間複雜度不超過。可以看出,相比於順序查詢,二分查詢的優化意義重大。
2.5 定性分析
對於二分查詢時間複雜度的計算,主要分為:元素的大小比較、秩的算術運算以及賦值。由於“秩”是無符號的整數,而“元素”通常更為複雜(第7章中有很多這樣的例子),甚至比較的複雜度可能不是(如對有序字典向量中進行匹配查詢),因此優先考慮比較的權重,整體的效率因此取決於比較操作的次數,這裡稱作“查詢長度”。
2.5.1 平均成功查詢長度
對於長度為n的有序向量,不失一般地,假設,也就是說為二分查詢樹的層數,假定查詢目標元素等概率分佈,因此我們需要求得此時平均成功查詢長度為
其中為第個元素的查詢長度,查詢長度總和為,我們可以得到關係
特別地,當時,成功查詢只有一種情況,因此
下面用“遞推分析”求出與的關係。
對於長度為的有序向量,每一步迭代都有三種可能:
● 左半邊元素:經過次成功的比較後原問題轉換為一個規模的子問題;
● 右半邊元素:經過次失敗的比較和次成功的比較後(共次)原問題轉換為一個規模的子問題;
● 中間的元素:經過次失敗的比較後在點命中,演算法終止。
根據上面的分析,我們可以得到如下遞推式
因為指向量中個元素的查詢長度的總和,因此的係數僅為1。
令,則,因而有
對上式做驗證:
我們可以得到
因此
從而有
亦即平均成功查詢長度為。
2.5.2 平均失敗查詢長度
根據迭代版程式碼,失敗查詢的終止條件是,亦即有效區間寬度縮減為時失敗告終。不難看出,對於對長度為向量進行的二分查詢,失敗的情況有種。
可以證明,一般情況下平均失敗查詢長度不超過。
我們用數學歸納法來證明這一結論。
(1)樸素情況:當時,平均失敗查詢長度為,此時結論成立。
(2)一般情況:假設當時結論成立,我們要證明當和當時結論也成立。
▲ 當時,左區間長度為,右區間長度為。
左側區間向量總共包含種失敗情況,根據歸納假設,其平均長度不超過;
右側區間向量總共包含種失敗情況,根據歸納假設,其平均長度不超過。
(後續內容待補充)
參考文獻
[1] 鄧俊輝. 資料結構(C++語言版). 北京:清華大學出版社, 2013年9月第3版, ISBN:9-787-302-330646
[2] 鄧俊輝. 資料結構習題解析. 北京:清華大學出版社, 2013年9月第3版, ISBN: 9-787-302-330653
相關文章
- 減治思想——二分查詢詳細總結
- 二分查詢 | 二分查詢的一種推薦寫法
- 二分查詢(一)——純粹的二分查詢
- 基礎二分查詢總結
- 查詢——二分查詢
- 【資料結構】折半查詢(二分查詢)資料結構
- 二分查詢實現----面試總結面試
- 提高查詢速度方法總結
- Python查詢-二分查詢Python
- 查詢演算法__二分查詢演算法
- 順序查詢和二分查詢
- SQL總結(一)基本查詢SQL
- 轉:C++實現的變種二分查詢法(折半查詢)--二叉查詢樹C++
- MongoDB查詢總結MongoDB
- SQL查詢總結SQL
- 二分查詢
- 查詢演算法之二分查詢演算法
- [資料結構] 二分查詢 (四種寫法)資料結構
- 查詢之折半查詢
- 查詢演算法集:順序查詢、二分查詢、插值查詢、動態查詢(陣列實現、連結串列實現)演算法陣列
- 如何找東西?查詢演算法之順序查詢和二分查詢詳解演算法
- 查詢(1)--靜態查詢
- [演算法]各種二分查詢演算法
- js精準查詢與模糊查詢,總有一種適合你的需求JS
- PHP二分查詢PHP
- 二分查詢法
- 陣列的查詢(搜尋):線性查詢和二分法查詢陣列
- 7-1 二分查詢 (20分)
- 二分查詢【折半查詢】演算法 PHP 版演算法PHP
- 聊一聊二分查詢法
- 一、OC,Swift 二分查詢法Swift
- 二分查詢及其變種演算法演算法
- 減少對錶的查詢
- 二分查詢的定義
- 【SQL查詢】集合查詢之INTERSECTSQL
- 資料結構與演算法整理總結---二分查詢資料結構演算法
- Oracle閃回查詢,閃回版本查詢與閃回事務查詢的使用區別總結Oracle
- 牛客網 查詢(二分查詢、北郵機試)