二分查找的注意事项
二分查找的各種情況實現以及一些注意點
標簽:?二分查找編程之美面試詳細解答算法 2015-04-28 20:38?539人閱讀?評論(2)?收藏?舉報 ?分類:版權聲明:本文為博主原創文章,未經博主允許不得轉載。
轉載請注明出處,謝謝:
http://blog.csdn.NET/u014285517/article/details/45341741
說真的自己開始也認為二分查找實在太簡單了,不屑一顧,可是上禮拜阿里實習面試真是教會我做人要踏實啊!上禮拜面試的事等下有時間再寫一篇文章細談,現在談談二分查找,建議大家先自己寫寫,然后再看下面的文章,效果更好。
? 讓我們先看看一個樸素版的二分查找(其實也不算太樸素,不過因為這部分網上以及編程之美里都有詳細描述以及代碼實現,所以也就顯得比較樸素了,注意事項會在代碼注釋里說明):?
[cpp]?view plaincopy
- /*?
- 最樸素的二分查找:找數組a的下標s到下標e查找元素t??
- */??
- #include<stdio.h>??
- ??
- int?binarySearch(int?a[],int?s,int?e,int?t)?{??
- ????int?i?=?s,j?=?e,mid;??
- ????while(i?<=?j)?{??
- ?????/*??
- ????(1)寫成(i+j)/2,可能會導致求和中間結果的溢出,因為兩個32位整數的和是有可能超過32位整數的范圍的嘛??
- ????(2)因為四則運算符的優先級高于移位運算符>>,所以可以把mid?=?i+((j-i)>>1)簡化下,不過注意不要寫成mid?=?i+(j-i)>>1哈??
- ????(3)因為移位運算符比直接除快嘛,所以用>>1代替除2??
- ????*/??
- ????????mid?=?i+(j-i>>1);//(i+j)/2,寫成這樣可能會溢出???
- ????????if(a[mid]?==?t)?{??
- ????????????return?mid;??
- ????????}?else?{??
- ????????????if(a[mid]?>?t)?{??
- ????????????????j?=?mid-1;??
- ????????????}?else?{??
- ????????????????i?=?mid+1;??
- ????????????}??
- ????????}??
- ????}??
- ????return?-1;????????
- }??
- ??
- int?main()?{??
- ????int?a[10]?=?{1,3,5,6,7,8,9,12,13,14};??
- ????int?t;??
- ????if((t?=?binarySearch(a,0,9,7))?==?-1)?{??
- ????????printf("不存在\n");??
- ????}?else?{??
- ????????printf("存在,下標為%d\n",t);??
- ????}??
- }??
上面的代碼還有什么問題呢?大家可以先想想,再往下看哈。
顯然如果要查找的數在數組中存在著重復元素,而且我們還想返回該數的最小或最大下標,顯然上面的程序是無法做到的,因為它一旦找到一個滿足的就退出了。
下面我們來想想怎么解決這個問題。 最容易想到的辦法就是在數組中找到某一個數后,直接不斷循環減(加),來尋找滿足條件的最小下標(最大下標)。相應代碼如下:
[cpp]?view plaincopy
- #include<stdio.h>??
- ??
- int?binarySearch(int?a[],int?s,int?e,int?t)?{??
- ????int?i?=?s,j?=?e,mid;??
- ????while(i?<=?j)?{??
- ????????mid?=?i+(j-i>>1);??
- ????????if(a[mid]?==?t)?{//關鍵部分代碼(查找數組中等于t的數的最小下標,最大下標同理)??
- ????????????while(a[--mid]?==?t)?{??
- ??????????????????
- ????????????}??
- ????????????return?mid+1;??
- ????????}?else?{??
- ????????????if(a[mid]?>?t)?{??
- ????????????????j?=?mid-1;??
- ????????????}?else?{??
- ????????????????i?=?mid+1;??
- ????????????}??
- ????????}??
- ????}??
- ????return?-1;????????
- }??
- ??
- int?main()?{??
- ????int?a[10]?=?{1,3,5,9,9,9,9,12,13,14};??
- ????int?t;??
- ????if((t?=?binarySearch(a,0,9,9))?==?-1)?{??
- ????????printf("不存在\n");??
- ????}?else?{??
- ????????printf("存在,下標為%d\n",t);??
- ????}??
- }??
現在可以想想上面的代碼存在什么問題嗎?
顯然如果重復的元素過多,二分查找搞不好就直接退化成暴力查找了,所以是不太好的。大家可以自己先寫寫解決代碼。
然后后看下面代碼:
[cpp]?view plaincopy
- #include<stdio.h>??
- ??
- int?binarySearch(int?a[],int?s,int?e,int?t)?{??
- ????int?i?=?s,j?=?e,mid;??
- ????while(i?<?j)?{??
- ????????mid?=?i+(j-i>>1);??
- ????????if(a[mid]?>=?t)?{//關鍵點:并不是等于就直接退出循環??
- ????????????j?=?mid;??
- ????????}?else?{??
- ????????????i?=?mid+1;??
- ????????}??
- ????}??
- ????if(a[i]?==?t)?{??
- ????????return?i;???
- ????}?else?{??
- ????????return?-1;????
- ????}?????????
- }??
- ??
- int?main()?{??
- ????int?a[10]?=?{1,3,9,9,9,9,9,9,13,14};??
- ????int?t;??
- ????if((t?=?binarySearch(a,0,9,9))?==?-1)?{??
- ????????printf("不存在\n");??
- ????}?else?{??
- ????????printf("存在,下標為%d\n",t);??
- ????}??
- }??
下面代碼是找最大下標的:
[cpp]?view plaincopy
- /*?
- 在一個非遞減數組中(就是數組中存在相等的元素),用二分查找求?
- 最大的下標i,使得a[i]?=?t,存在返回下標i,不存在返回-1??
- */???
- #include<stdio.h>??
- ??
- int?binarySearch(int?a[],int?s,int?e,int?t)?{??
- ????int?i?=?s,j?=?e,mid;??
- ????while(i?<?j)?{??
- ????????mid?=?i+(j-i+1>>1);??
- ????????if(t?>=?a[mid])?{//關鍵點:并不是等于就直接退出循環?i?=?mid??
- ????????????i?=?mid;??
- ????????}?else?{??
- ????????????j?=?mid-1;??
- ????????}??
- ????}??
- ????if(a[i]?==?t)?{??
- ????????return?i;???
- ????}?else?{??
- ????????return?-1;????
- ????}?????????
- }??
- ??
- int?main()?{??
- ????int?a[10]?=?{1,9,9,9,9,9,9,12,13,14};??
- ????int?t;??
- ????if((t?=?binarySearch(a,0,9,9))?==?-1)?{??
- ????????printf("不存在\n");??
- ????}?else?{??
- ????????printf("存在,下標為%d\n",t);??
- ????}??
- }??
下面我們再來思考兩個類似問題,加深理解。
在一個非遞減數組中(就是數組中可能存在相等的元素),求最小的下標i,使得a[i] > t,存在返回下標i,不存在返回-1。?
實現代碼如下:
[cpp]?view plaincopy
- /*?
- 在一個非遞減數組中(就是數組中存在相等的元素),用二分查找求?
- 最小的下標i,使得a[i]?>?t,存在返回下標i,不存在返回-1??
- */???
- #include<stdio.h>??
- ??
- int?binarySearch(int?a[],int?s,int?e,int?t)?{??
- ????int?i?=?s,j?=?e,mid;??
- ????while(i?<?j)?{??
- ????????mid?=?i+(j-i>>1);??
- ????????if(t?>=?a[mid])?{??
- ????????????i?=?mid+1;??
- ????????}?else?{??
- ????????????j?=?mid;??
- ????????}??
- ????}??
- ????if(a[i]?>?t)?{??
- ????????return?i;???
- ????}?else?{??
- ????????return?-1;????
- ????}?????????
- }??
- ??
- int?main()?{??
- ????int?a[10]?=?{1,9,9,9,9,9,9,12,13,14};??
- ????int?t;??
- ????if((t?=?binarySearch(a,0,9,8))?==?-1)?{??
- ????????printf("不存在\n");??
- ????}?else?{??
- ????????printf("存在,下標為%d\n",t);??
- ????}??
- }???
另一個類似問題:
在一個非遞減數組中(就是數組中可能存在相等的元素),求最大的下標i,使得a[i] < t,存在返回下標i,不存在返回-1。?
實現代碼如下:
[cpp]?view plaincopy
- /*?
- 在一個非遞減數組中(就是數組中存在相等的元素),用二分查找求?
- 最大的下標i,使得a[i]?<?t,存在返回下標i,不存在返回-1??
- */???
- #include<stdio.h>??
- ??
- int?binarySearch(int?a[],int?s,int?e,int?t)?{??
- ????int?i?=?s,j?=?e,mid;??
- ????while(i?<?j)?{??
- ????????mid?=?i+(j-i+1>>1);//j-i>>1改成了j-i+1>>1,避免出現t比s~e區間內所有元素都大時,跳不出循環的bug<span?style="font-family:?Arial,?Helvetica,?sans-serif;">??
- </span>?????if(t?<=?a[mid])?{??
- ????????????j?=?mid-1;??
- ????????}?else?{??
- ????????????i?=?mid;??
- ????????}??
- ????}??
- ????if(a[i]?<?t)?{??
- ????????return?i;???
- ????}?else?{??
- ????????return?-1;????
- ????}?????????
- }??
- ??
- int?main()?{??
- ????int?a[10]?=?{1,9,9,9,9,9,9,12,13,14};??
- ????int?t;??
- ????if((t?=?binarySearch(a,0,9,10))?==?-1)?{??
- ????????printf("不存在\n");??
- ????}?else?{??
- ????????printf("存在,下標為%d\n",t);??
- ????}??
- }??
如果把上面兩個問題改成下面這樣:?
1.在一個非遞減數組中(就是數組中存在相等的元素),求最**大**的下標i,使得a[i] **>** t,存在返回下標i,不存在返回-1。?
2.在一個非遞減數組中(就是數組中存在相等的元素),求最**小**的下標i,使得a[i] **<** t,存在返回下標i,不存在返回-1。?
大家稍微想下應該能發現這種問題意義不大,我這里就不細說了。?
注:上面代碼都是自己寫的,如果有bug歡迎指出。上面說的一些問題如果有錯誤也歡迎指出,謝謝。
補充Java寫的一個遞歸實現代碼:
[java]?view plaincopy
- //遞歸實現??
- public?void?binarySearch(int[]?a,?int?x,?int?beginIndex,?int?endIndex)?{??
- ????int?len?=?endIndex?-?beginIndex?+?1;??
- ????if?(endIndex?<?beginIndex)?{//防止進入無限遞歸??
- ????????System.out.println("not?find");??
- ????????return;??
- ????}??
- ????if?(a[beginIndex?+?len?/?2]?==?x)?{??
- ????????System.out.println("find?index?is:"?+?(beginIndex?+?len?/?2));??
- ????????return;??
- ????}?else?{??
- ????????if?(x?>?a[beginIndex?+?len?/?2])?{??
- ????????????binarySearch(a,?x,?beginIndex?+?len?/?2?+?1,?endIndex);??
- ????????}?else?{??
- ????????????binarySearch(a,?x,?beginIndex,?beginIndex?+?len?/?2?-?1);??
- ????????}??
- ????}??
- }??
總結
- 上一篇: hdu 2665(主席树查询区间k大值)
- 下一篇: 树形动态规划