二分搜索及其扩展(循环递增数组的搜索)
生活随笔
收集整理的這篇文章主要介紹了
二分搜索及其扩展(循环递增数组的搜索)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
二分搜索需要注意開閉區間的問題,限制條件和邊界要保持配對:low<=high , low = mid +1? ,high = mid-1。
二分搜索的模板如下:
// 二分搜索 int BinarySearch(int *num, int key, int low, int high) {int mid ;while(low <= high) //切記:條件是 <= ,很多次都不小心寫成了 < ,導致了N多WA{mid = (low + high)>>1;if(num[mid] == key)return mid;else if(num[mid] < key)low = mid + 1;elsehigh = mid -1;}return low; //查找不成功時,返回應該插入的位置,或者直接返回-1,表示查找失敗也是可以的 } ????? 二分搜索的擴展:對已排好序的數組A,一般來說可用二分查找 可以很快找到。現有一特殊數組A[],它是循環遞增的,如A[]={ 17 19 20 25 1 4 7 9},試在這樣的數組中找一元素x,看看是否存在。請寫出你的算法,必要時可寫偽代碼,并分析其空間、時間復雜度。
????? 對于循環有序的數組,它是循環遞增的,例如:A[]={ 17 19 20 25 1 4 7 9},也可以使用二分搜索:每次將數組分為2部分,一個是單調遞增的,一個是循環遞增的。如果是單調遞增的話直接使用樸素的二分搜索,如果是循環遞增的話繼續使用特殊二分搜索下去。
思路分析:
????? 在一個數組中檢索一個元素,最普通的算法就是時間復雜度為O(n)的順序檢索。要降低時間復雜度,并結合循環遞增數組的元素已有一定程度順序的性質,很容易想到二分查找。
????? 我最初的想法是:樸素的二分查找在是下標區間[0 n-1]內進行二分,那么利用循環遞增的性質,我們可以在數組的有效下標區間[0 n-1]的一個偏移區間[r+1 n+r-1]里進行二分(其中r是上述循環遞增數組定義中的那個分界下標r)。但是,這個想法的一個最大問題是:給定一個循環遞增數組,我如何去確定其分界下標r?最明顯的做法就是順序掃描一遍數組,然后確定其分界下標r。但是,既然你都掃描一遍了,也就能確定檢索元素是否在該數組中了,何必再去利用分界下標r去檢索呢?
????? 上述想法不成功,我們再去深入地去思考二分查找方法的一些原理特性。在一個嚴格遞增的數組中,我們將要檢索的元素和數組中間的元素進行比較,然后根據要檢索的元素與數組中間的元素的大小關系來確定該元素落在那個范圍內,然后遞歸地在該范圍內進行檢索。
????? 現在在循環遞增數組中,我們不能簡單地通過與數組中間元素的大小關系來確定要檢索的元素所落在的區間范圍。要確定范圍,我們可以再加上要檢索的元素與數組兩端的元素的大小關系。
????? 循環遞增數組有這么一個性質:以數組中間元素將循環遞增數組劃分為兩部分,則一部分為一個嚴格遞增數組,而另一部分為一個更小的循環遞增數組。當中間元素大于首元素時,前半部分為嚴格遞增數組,后半部分為循環遞增數組;當中間元素小于首元素時,前半部分為循環遞增數組;后半部分為嚴格遞增數組。
記要檢索的元素為key,數組的首元素為a[low],中間元素為a[mid],末尾元素為a[high]。則當key不等于a[mid] 時,
??? 1、a[mid] > a[low],即數組前半部分為嚴格遞增數組,后半部分為循環遞增數組時,若key小于a[mid]并且不小于a[low]時,則key落在數組前半部分;否則,key落在數組后半部分。
??? 2、a[mid] < a[high],即數組前半部分為循環遞增數組,后半部分為嚴格遞增數組時,若key大于a[mid]并且不大于a[high]時,則key落在數組后半部分;否則,key落在數組前半部分。
通過上面利用數組首元素,中間元素和末尾元素確定要檢索的元素所在范圍,我們就可以使用修改后的二分查找算法了。
實現代碼如下:
// 改進后的二分搜索 int Search(int A[],int n, int num) {int left = 0 ;int right = n - 1 ;int mid = 0 ;int pos = -1 ; //返回-1,表示查找失敗while(left <= right){mid = (left + right)/2 ;if (A[mid] == num){ pos = mid ; break; }if (A[left] <= A[mid]) //前半部分是嚴格遞增的,后半部分是一個更小的循環遞增數組{if(A[left] <= num && num < A[mid] ){right = mid - 1 ;}else{left = mid + 1 ;} }else //后半部分是嚴格遞增的,前半部分是一個更小的循環遞增數組{if ( A[mid] < num && num <= A[right]){left = mid + 1 ;}else{right = mid - 1 ;}}}return pos; }
二分搜索的模板如下:
// 二分搜索 int BinarySearch(int *num, int key, int low, int high) {int mid ;while(low <= high) //切記:條件是 <= ,很多次都不小心寫成了 < ,導致了N多WA{mid = (low + high)>>1;if(num[mid] == key)return mid;else if(num[mid] < key)low = mid + 1;elsehigh = mid -1;}return low; //查找不成功時,返回應該插入的位置,或者直接返回-1,表示查找失敗也是可以的 } ????? 二分搜索的擴展:對已排好序的數組A,一般來說可用二分查找 可以很快找到。現有一特殊數組A[],它是循環遞增的,如A[]={ 17 19 20 25 1 4 7 9},試在這樣的數組中找一元素x,看看是否存在。請寫出你的算法,必要時可寫偽代碼,并分析其空間、時間復雜度。
????? 對于循環有序的數組,它是循環遞增的,例如:A[]={ 17 19 20 25 1 4 7 9},也可以使用二分搜索:每次將數組分為2部分,一個是單調遞增的,一個是循環遞增的。如果是單調遞增的話直接使用樸素的二分搜索,如果是循環遞增的話繼續使用特殊二分搜索下去。
思路分析:
????? 在一個數組中檢索一個元素,最普通的算法就是時間復雜度為O(n)的順序檢索。要降低時間復雜度,并結合循環遞增數組的元素已有一定程度順序的性質,很容易想到二分查找。
????? 我最初的想法是:樸素的二分查找在是下標區間[0 n-1]內進行二分,那么利用循環遞增的性質,我們可以在數組的有效下標區間[0 n-1]的一個偏移區間[r+1 n+r-1]里進行二分(其中r是上述循環遞增數組定義中的那個分界下標r)。但是,這個想法的一個最大問題是:給定一個循環遞增數組,我如何去確定其分界下標r?最明顯的做法就是順序掃描一遍數組,然后確定其分界下標r。但是,既然你都掃描一遍了,也就能確定檢索元素是否在該數組中了,何必再去利用分界下標r去檢索呢?
????? 上述想法不成功,我們再去深入地去思考二分查找方法的一些原理特性。在一個嚴格遞增的數組中,我們將要檢索的元素和數組中間的元素進行比較,然后根據要檢索的元素與數組中間的元素的大小關系來確定該元素落在那個范圍內,然后遞歸地在該范圍內進行檢索。
????? 現在在循環遞增數組中,我們不能簡單地通過與數組中間元素的大小關系來確定要檢索的元素所落在的區間范圍。要確定范圍,我們可以再加上要檢索的元素與數組兩端的元素的大小關系。
????? 循環遞增數組有這么一個性質:以數組中間元素將循環遞增數組劃分為兩部分,則一部分為一個嚴格遞增數組,而另一部分為一個更小的循環遞增數組。當中間元素大于首元素時,前半部分為嚴格遞增數組,后半部分為循環遞增數組;當中間元素小于首元素時,前半部分為循環遞增數組;后半部分為嚴格遞增數組。
記要檢索的元素為key,數組的首元素為a[low],中間元素為a[mid],末尾元素為a[high]。則當key不等于a[mid] 時,
??? 1、a[mid] > a[low],即數組前半部分為嚴格遞增數組,后半部分為循環遞增數組時,若key小于a[mid]并且不小于a[low]時,則key落在數組前半部分;否則,key落在數組后半部分。
??? 2、a[mid] < a[high],即數組前半部分為循環遞增數組,后半部分為嚴格遞增數組時,若key大于a[mid]并且不大于a[high]時,則key落在數組后半部分;否則,key落在數組前半部分。
通過上面利用數組首元素,中間元素和末尾元素確定要檢索的元素所在范圍,我們就可以使用修改后的二分查找算法了。
實現代碼如下:
// 改進后的二分搜索 int Search(int A[],int n, int num) {int left = 0 ;int right = n - 1 ;int mid = 0 ;int pos = -1 ; //返回-1,表示查找失敗while(left <= right){mid = (left + right)/2 ;if (A[mid] == num){ pos = mid ; break; }if (A[left] <= A[mid]) //前半部分是嚴格遞增的,后半部分是一個更小的循環遞增數組{if(A[left] <= num && num < A[mid] ){right = mid - 1 ;}else{left = mid + 1 ;} }else //后半部分是嚴格遞增的,前半部分是一個更小的循環遞增數組{if ( A[mid] < num && num <= A[right]){left = mid + 1 ;}else{right = mid - 1 ;}}}return pos; }
總結
以上是生活随笔為你收集整理的二分搜索及其扩展(循环递增数组的搜索)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2012 人民搜索 实习生招聘 笔试题
- 下一篇: 经典面试题:链表的相交与环问题