二分查找及其几个变形

 

View Code
  1 #include <iostream>
  2 #include <cassert>
  3 
  4 using namespace std;
  5 
  6 //给定一个非降序有序数组,返回出现value的任意
  7 //一位置,不存在则返回-1
  8 //low:a中第一个元素的下标
  9 //high:a中最后一个元素的下标
 10 //如果在a中有多个value,每次返回的都是value第一次
 11 //出现的下标
 12 int BinarySearch1(int *a,int low,int high,int value)
 13 {
 14     if (!a || low>high || value<a[low] || value>a[high])
 15     {
 16         return -1;
 17     }
 18     int middle=low;
 19     low--;
 20     high++;
 21     //low--最初是为了对a中只有两个元素时也能进入while循环
 22     //但是low--之后,middle=low+((high-low)>>1);计算得到值
 23     //并不是数组a的中间位置的值,为了平衡low--,所以让high++
 24     //这样计算得到的就是数组的中间位置的值了。high++的另外一个
 25     //好处就是当a中只有一个元素时,while循环也是满足的。
 26     while(low+1!=high)
 27     {
 28         middle=low+((high-low)>>1);
 29         //由于value>a[middle]的条件约束,如果找到value,那么
 30         //对应的下标肯定在high端,low端所对应的元素值都比value小。
 31         if (value>a[middle])
 32         {
 33             low=middle;
 34         }
 35         else
 36         {
 37             high=middle;
 38         }
 39     }
 40     //由于low+1!=high的条件约束,当high和low之间只存在两个元素时,
 41     //此时low对应第一个元素,high对应第二个元素,不会进入while循环
 42     //也就不能对high元素是否等于value进行判断。所以需要下面的if判断
 43     //a[high]!=value。当a中的所有元素都不要查找的元素小时
 44     if (a[high]!=value)
 45     {
 46         high=-1;
 47     }
 48     return high;
 49 }
 50 
 51 
 52 //给定一个非降序有序数组,返回出现value的最后
 53 //一个位置,不存在则返回-1
 54 int BinarySearch2(int *a,int low,int high,int value)
 55 {
 56     if (!a || low>high || value<a[low] || value>a[high])
 57     {
 58         return -1;
 59     }
 60     int middle=low;
 61     low--;
 62     high++;
 63     while(low+1!=high)
 64     {
 65         middle=low+((high-low)>>1);
 66         //由于value<a[middle]的条件约束,如果找到value,那么
 67         //对应的下标肯定在low端,high端所对应的元素值都比value大。
 68         if (value<a[middle])
 69         {
 70             high=middle;
 71         }
 72         else
 73         {
 74             low=middle;
 75         }
 76     }
 77     //这里要对a[low]进行探测
 78     if (a[low]!=value)
 79     {
 80         low=-1;
 81     }
 82     return low;
 83 }
 84 
 85 //辅助方法:二分查找
 86 //输出最小的i,使得a[i]大于value.
 87 //如果所有a[i]均小于value,则返回
 88 //high+1。如果出现错误返回-1
 89 int BinarySearch3(int* a,int low,int high,int value)
 90 {
 91     //边界条件检查
 92     if (!a || low>high)
 93     {
 94         return -1;
 95     }
 96     //下面两个if语句在while循环中都会处理这两种情况
 97     //之所以单独提出来,是因为这两种情况无需进入while
 98     //循环,直接用if语句就可以处理了。
 99     //if (value>elem[high])
100     //{
101     //    return high+1;
102     //}
103     //if (value<elem[low])
104     //{
105     //    return low;
106     //}
107     //这里之所以令low减1是为了这种情况考虑:
108     //当high-low=1时,即只有两个元素,那么
109     //while循环就不会满足,所以令low减1。
110     low--;
111     high++;
112     int middle;
113     //这里如果high的值没有改变,说明a中的所有数字都比
114     //value大,那出了循环直接返回high就可以了。这个时候
115     //不需要像查找value在a中是否存在那样,while执行之后
116     //使用if条件判断。这里不要if判断了。
117     while (low+1!=high)
118     {
119         middle=low+((high-low)>>1);
120         if (a[middle]>value)
121         {
122             high=middle;
123         }
124         else
125         {
126             low=middle;
127         }
128     }
129     return high;
130 }
131 
132 
133 //辅助方法:二分查找
134 //输出最大的i,使得a[i]小于value.
135 //如果所有a[i]均大于value,则返回
136 //low-1。如果出现错误返回-2
137 //因为low-1可能等于-1,所以这里用-2
138 //作为错误码
139 int BinarySearch4(int* a,int low,int high,int value)
140 {
141     //边界条件检查
142     if (!a || low>high)
143     {
144         return -2;
145     }
146     //下面两个if语句在while循环中都会处理这两种情况
147     //之所以单独提出来,是因为这两种情况无需进入while
148     //循环,直接用if语句就可以处理了。
149     //if (value>elem[high])
150     //{
151     //    return high;
152     //}
153     //if (value<elem[low])
154     //{
155     //    return low-1;
156     //}
157     low--;
158     high++;
159     int middle;
160     //这里如果high的值没有改变,说明a中的所有数字都比
161     //value大,那出了循环直接返回high就可以了。这个时候
162     //不需要像查找value在a中是否存在那样,while执行之后
163     //使用if条件判断。这里不要if判断了。
164     while (low+1!=high)
165     {
166         middle=low+((high-low)>>1);
167         if (a[middle]<value)
168         {
169             low=middle;
170         }
171         else
172         {
173             high=middle;
174         }
175     }
176     return low;
177 }
178 
179 //给定一个非降序有序数组,输出最接近value
180 //的两个值的下标
181 pair<int,int> BinarySearch5(int *a,int low,int high,int value)
182 {
183     //边界条件检查
184     if (!a || low>high)
185     {
186         return pair<int,int>(-2,-2);
187     }
188     //这里之所以令low减1是为了这种情况考虑:
189     //当high-low=1时,即只有两个元素,那么
190     //while循环就不会满足,所以令low减1。
191     low--;
192     high++;
193     int middle;
194     //这里如果high的值没有改变,说明a中的所有数字都比
195     //value大,那出了循环直接返回high就可以了。这个时候
196     //不需要像查找value在a中是否存在那样,while执行之后
197     //使用if条件判断。这里不要if判断了。
198     while (low+1!=high)
199     {
200         middle=low+((high-low)>>1);
201         if (a[middle]<value)
202         {
203             low=middle;
204         }
205         else
206         {
207             high=middle;
208         }
209     }
210     pair<int,int> pos(high,high);
211     if (a[high]!=value)
212     {
213         pos.first=low;
214     }
215     return pos;
216 }
217 
218 
219 int main()
220 {
221     const int aLength=7;
222     int a1[aLength]={1,2,3,4,5,6,8};
223     int a2[aLength]={1,1,1,1,1,1,1};
224     int a3[aLength]={1,1,2,2,2,3,3};
225     int a4[aLength]={1,2,3,3,4,4,5};
226     cout<<"返回value第一次出现的位置:"<<endl;
227     for (int i=0;i<aLength;i++)
228     {
229         cout<<BinarySearch1(a1,0,aLength-1,a1[i])<<"  ";
230     }
231     cout<<endl;
232     for (int i=0;i<aLength;i++)
233     {
234         cout<<BinarySearch1(a2,0,aLength-1,a2[i])<<"  ";
235     }
236     cout<<endl;
237     for (int i=0;i<aLength;i++)
238     {
239         cout<<BinarySearch1(a3,0,aLength-1,a3[i])<<"  ";
240     }
241     cout<<endl;    
242     for (int i=0;i<aLength;i++)
243     {
244         cout<<BinarySearch1(a4,0,aLength-1,a4[i])<<"  ";
245     }
246     cout<<endl;
247     cout<<BinarySearch1(a1,0,aLength-1,7)<<endl;
248     
249     cout<<"返回value最后一次出现的位置:"<<endl;
250     for (int i=0;i<aLength;i++)
251     {
252         cout<<BinarySearch2(a1,0,aLength-1,a1[i])<<"  ";
253     }
254     cout<<endl;
255     for (int i=0;i<aLength;i++)
256     {
257         cout<<BinarySearch2(a2,0,aLength-1,a2[i])<<"  ";
258     }
259     cout<<endl;
260     for (int i=0;i<aLength;i++)
261     {
262         cout<<BinarySearch2(a3,0,aLength-1,a3[i])<<"  ";
263     }
264     cout<<endl;    
265     for (int i=0;i<aLength;i++)
266     {
267         cout<<BinarySearch2(a4,0,aLength-1,a4[i])<<"  ";
268     }
269     cout<<endl;
270     cout<<BinarySearch2(a1,0,aLength-1,7)<<endl;
271 
272     cout<<"返回值大于value的最小的下标:"<<endl;
273     for (int i=0;i<aLength;i++)
274     {
275         cout<<BinarySearch3(a1,0,aLength-1,a1[i])<<"  ";
276     }
277     cout<<endl;
278     for (int i=0;i<aLength;i++)
279     {
280         cout<<BinarySearch3(a2,0,aLength-1,a2[i])<<"  ";
281     }
282     cout<<endl;
283     for (int i=0;i<aLength;i++)
284     {
285         cout<<BinarySearch3(a3,0,aLength-1,a3[i])<<"  ";
286     }
287     cout<<endl;    
288     for (int i=0;i<aLength;i++)
289     {
290         cout<<BinarySearch3(a4,0,aLength-1,a4[i])<<"  ";
291     }
292     cout<<endl;
293     cout<<BinarySearch3(a1,0,aLength-1,7)<<endl;
294 
295     cout<<"返回值小于value的最大的下标:"<<endl;
296     for (int i=0;i<aLength;i++)
297     {
298         cout<<BinarySearch4(a1,0,aLength-1,a1[i])<<"  ";
299     }
300     cout<<endl;
301     for (int i=0;i<aLength;i++)
302     {
303         cout<<BinarySearch4(a2,0,aLength-1,a2[i])<<"  ";
304     }
305     cout<<endl;
306     for (int i=0;i<aLength;i++)
307     {
308         cout<<BinarySearch4(a3,0,aLength-1,a3[i])<<"  ";
309     }
310     cout<<endl;    
311     for (int i=0;i<aLength;i++)
312     {
313         cout<<BinarySearch4(a4,0,aLength-1,a4[i])<<"  ";
314     }
315     cout<<endl;
316     cout<<BinarySearch4(a1,0,aLength-1,7)<<endl;
317 
318     cout<<"返回值最接近value的两个下标,如果存在value就两个值同时为value的下标:"<<endl;
319     pair<int,int> pos;
320     for (int i=0;i<aLength;i++)
321     {
322         pos=BinarySearch5(a1,0,aLength-1,a1[i]);
323         cout<<"("<<pos.first<<","<<pos.second<<")"<<"  ";
324     }
325     cout<<endl;
326     for (int i=0;i<aLength;i++)
327     {
328         pos=BinarySearch5(a2,0,aLength-1,a2[i]);
329         cout<<"("<<pos.first<<","<<pos.second<<")"<<"  ";
330     }
331     cout<<endl;
332     for (int i=0;i<aLength;i++)
333     {
334         pos=BinarySearch5(a3,0,aLength-1,a3[i]);
335         cout<<"("<<pos.first<<","<<pos.second<<")"<<"  ";
336     }
337     cout<<endl;
338     for (int i=0;i<aLength;i++)
339     {
340         pos=BinarySearch5(a4,0,aLength-1,a4[i]);
341         cout<<"("<<pos.first<<","<<pos.second<<")"<<"  ";
342     }
343     cout<<endl;
344     pos=BinarySearch5(a1,0,aLength-1,7);
345     cout<<"("<<pos.first<<","<<pos.second<<")"<<"  ";
346     int a5[aLength]={1,5,9,9,13,20,25};
347     pos=BinarySearch5(a5,0,aLength-1,0);
348     cout<<"("<<pos.first<<","<<pos.second<<")"<<"  ";
349     pos=BinarySearch5(a5,0,aLength-1,4);
350     cout<<"("<<pos.first<<","<<pos.second<<")"<<"  ";
351     pos=BinarySearch5(a5,0,aLength-1,7);
352     cout<<"("<<pos.first<<","<<pos.second<<")"<<"  ";
353     pos=BinarySearch5(a5,0,aLength-1,9);
354     cout<<"("<<pos.first<<","<<pos.second<<")"<<"  ";
355     pos=BinarySearch5(a5,0,aLength-1,28);
356     cout<<"("<<pos.first<<","<<pos.second<<")"<<"  ";
357     cout<<endl;
358 }

 

《编程之美》3.11介绍了二分查找,并给出了几个思考问题,上面的代码对几个思考问题给出了解答:

在上述代码中需要注意的几点问题:

1. 为了避免整数的溢出问题,需要写出:middle=low+((high-low)>>1); 最好不要写成middle=(low+high)>>1;

2. 二分代码最需要考虑的就是边界问题,边界也是最容易出错的地方。

3. 上述代码有很多技巧及细节的处理,这也是结合《编程珠玑》第九章给出的编码。

4. 在while循环判断使用low+1!=high而不是使用low<=high,是因为使用后者很难找出最接近value的数。有可能middle最接近value,而每次更新是high=middle+1,low=middle-1,这样就把middle跳过去了。个人见解,欢迎拍砖。

5. 写一个正确高效的二分真得不容易!!!

6. 运行结果如下:

posted @ 2012-06-12 20:45  kasuosuo  阅读(268)  评论(0编辑  收藏  举报