搜索技术【二分搜索】 - 简介 原理
搜索技術【二分搜索】 - 簡介 & 原理
【舉個栗子】
某大型娛樂節目在玩猜數游戲,主持人在女嘉賓的手心寫一個10以內的整數,讓女嘉賓的老公猜數字是多少,而女嘉賓只能提示老公猜的數字是大了還是小了,并且只有3次機會。
主持人悄悄地在女嘉賓手心寫了一個8。
女嘉賓的老公: “2。” 女嘉賓: “小了?!?女嘉賓的老公: “3?!?女嘉賓: “小了。” 女嘉賓的老公: “10?!?女嘉賓: “還是沒猜對?!?那么,你有沒有辦法以最快的速度猜出來呢?
從問題描述來看,如果有n 個數,那么在最壞情況下需要猜n 次才能成功,其實完全沒有必要一個一個地猜,因為這些數是有序的,可以使用二分搜索策略,每次都和中間的元素做比較,如果比中間的元素小,則在前半部分查找;如果比中間的元素大,則在后半部分查找。
這種方法被稱為二分查找或折半查找,也被稱為二分搜索技術。
【原理】 - 二分搜索技術
例如,給定有n 個元素的序列,這些元素是有序的(假定為升序),從序列中查找元素x 。
用一維數組S []存儲該有序序列,設變量low和high表示查找范圍的下界和上界,middle表示查找范圍的中間位置,x 表示特定的查找元素。
[算法步驟]
① 初始化。令low=0,即指向有序數組S []的第1個元素;high=n ?1,即指向有序數組S []的最后一個元素。
② 判定low≤high是否成立,如果成立,則轉向步驟3,否則算法結束。
③ middle=(low+high)/2,即指向查找范圍的中間元素。如果數量較大,則為避免low+high溢出,可以采用middle=low+(high - low)/2。
④ 判斷x 與S [middle]的關系。如果x =S [middle],則搜索成功,算法結束;如果x >S [middle],則令low=middle+1;否則令high=middle?1,轉向步驟2。
[舉個栗子]
例如,在有序序列(5,8,15,17,25,30,34,39,45,52,60)中查找元素17。
① 數據結構。用一維數組S []存儲該有序序列,x =17。
② 初始化。low=0,high=10,計算middle=(low+high)/2=5。
③ 將x 與S [middle]做比較。x =17,S [middle]=30,在序列的前半部分查找,令high=middle?1,搜索的范圍縮小到子問題S [0…middle?1]。
④ 計算middle=(low+high)/2=2。
⑤ 將x 與S [middle]做比較。x =17,S [middle]=15,在序列的后半部分查找,令low=middle+1,搜索的范圍縮小到子問題S[middle+1…high]。
⑥ 計算middle=(low+high)/2=3。
⑦ 將x 與S [middle]做比較。x =S [middle]=17,查找成功,算法結束。
[算法實現]
用BinarySearch(int n , int s [], int x )函數實現二分查找算法,其中n 為元素個數,s []為有序數組,x 為待查找的元素。low指向數組的第1個元素,high指向數組的最后一個元素。如果low≤high,middle=(low+high)/2,即指向查找范圍的中間元素。如果x =S[middle],則搜索成功,算法結束;如果x >S [middle],則令low=middle+1,在后半部分搜索;否則令high=middle?1,在前半部分搜索。
① 非遞歸算法。
int BinarySearch(int s[] , int n , int s){ //二分查找非遞歸算法int low = 0, high = n - 1; //low指向數組的第1個元素,high指向數組的最后一個元素while(low <= high){int middle = (low + high) / 2; //middle為查找范圍的中間值if(x == s[middle]){ //x 等于查找范圍的中間值,算法結束return middle;}else if(x > s[middle]){ //x 大于查找范圍的中間元素,在后半部分查找low = middle + 1; }else{ //x小于查找范圍的中間元素,在前半部分查找high = middle - 1;}}return -1; }② 遞歸算法。遞歸有自調用問題,增加兩個參數low和high標記搜索范圍的開始和結束。
int recursionBS(int s[] . int x , int low ,int high){ //二分查找遞歸算法//low 指向搜索區間的第1個元素,high 指向搜索區間的最后一個元素if(low > high){ ///遞歸結束條件return -1;}int middle = (low + high) / 2; //計算middle值(查找范圍的中間值)if(x == s[middle]){ //x 等于s[middle],查找成功,算法結束return middle;}else if(x < s[middle]){ //x 小于s[middle],在前半部分查找return recursionBS(s , x ,low , middle - 1);}else{ //x 大于s[middle],在后半部分查找return recursionBS(s , x ,middle + 1 , high);} }[算法分析]
① 時間復雜度
怎么計算二分查找算法的時間復雜度呢?如果用T (n )來表示n 個有序元素的二分查找算法的時間復雜度,那么結果如下。
- 當n =1時,需要一次做比較,T (n )=O (1)。
- 當n >1時,將待查找元素和中間位置元素做比較,需要O (1)時間,如果比較不成功,那么需要在前半部分或后半部分搜索,問題的規??s小了一半,時間復雜度變為T (n /2)。
- 當n >1時,可以遞推求解如下:
遞推最終的規模為1,令n =2^x ,則x =log n 。
二分查找的非遞歸算法和遞歸算法查找的方法是一樣的,時間復雜度相同,均為O (logn )。
② 空間復雜度
在二分查找的非遞歸算法中,變量占用了一些輔助空間,這些輔助空間都是常數階的,因此空間復雜度為O (1)。
二分查找的遞歸算法,除了使用一些變量,還需要使用棧來實現遞歸調用。在遞歸算法中,每一次遞歸調用都需要一個??臻g存儲,我們只需看看有多少次調用即可。假設原問題的規模為n ,首先第1次遞歸就分為兩個規模為n /2的子問題,這兩個子問題并不是每個都執行,只會執行其中之一,因為與中間值做比較后,要么在前半部分查找,要么在后半部分查找;然后把規模為n /2的子問題繼續劃分為兩個規模為n/4的子問題,選擇其一;繼續分治下去,在最壞情況會分治到只剩下一個數值,那么算法執行的節點數就是從樹根到葉子所經過的節點,每一層執行一個,直到最后一層,如下圖所示。
遞歸調用最終的規模為1,即n /2 ^x =1,則x =logn 。假設陰影部分是搜索經過的路徑,一共經過了logn 個節點,也就是說遞歸調用了logn 次。遞歸算法使用的棧空間為遞歸樹的深度,因此二分查找遞歸算法的空間復雜度為O (logn )。在二分搜索中需要注意以下幾個問題。
① 必須滿足有序性。
② 搜索范圍。初始時,需要指定搜索范圍,如果不知道具體范圍,則對正數可以采用范圍[0,inf],對負數可以采用范圍[-inf,inf],inf為無窮大,通常設定為0x3f3f3f3f。
③ 二分搜索。在一般情況下,mid=(l +r )/2或mid=(l +r)>>1。如果l 和r 特別大,則為了避免l +r 溢出,可以采用mid=l +(r-l )/2。對判斷二分搜索結束的條件,以及判斷mid可行時是在前半部分搜索,還是在后半部分搜索,需要具體問題具體分析。
④ 答案是什么。在減少搜索范圍時,要特別注意是否漏掉了mid點上的答案。
====================================================
二分搜索分為整數上的二分搜索和實數上的二分搜索,大致過程如下。
① 整數上的二分搜索
整數上的二分搜索,因為縮小搜索范圍時,有可能r =mid-1或l=mid+1,因此可以用ans記錄可行解。對是否需要減1或加1,要根據具體問題來分析。
l = a ; r = b; //初始搜索范圍 while(l <= r){int mid = (l + r) / 2;if(judge(mid)){ans = mid; //記錄可行解r = mid - 1;}else{l = mid + 1;} } return ans;② 實數上的二分搜索
實數上的二分搜索不可以直接比較大小,可以將r -l >eps作為循環條件,eps為一個較小的數,例如1e-7等。為避免丟失可能解,縮小范圍時r =mid或l =mid,在循環結束時返回最后一個可行解。
l = a; r = b; //初始搜索范圍 while(r - l>eps){ //判斷差值double mid = (l + r) / 2;if(judge(mid)){l = mid; //l 記錄了可行解,在循環結束時返回答案l}else{r = mid;} } return l;還可以運行固定的次數,例如運行100次,可達10^-30 精度,在一般情況下都可以解決問題
l = a ; r = b; for(int i = 0 ; i < 100 ; i++){ //運行100次double mid = (l + r) / 2;if(judge(mid)){l = mid;}else{r = mid;} } return l;總結
以上是生活随笔為你收集整理的搜索技术【二分搜索】 - 简介 原理的全部內容,希望文章能夠幫你解決所遇到的問題。