二分查找中的死循环
来源:互联网 发布:java如何合并两个list 编辑:程序博客网 时间:2024/04/27 13:54
二分算法是我们经常会用到的一个算法。它是分治法的一个应用。不过,虽然他写起来貌似很简单,但是却很容易写错。下面我们讨论一下二分的死循环问题。(这里讨论的是整数的二分问题,浮点数的二分不容易死循环)
1.查找的元素确定,值唯一或者不存在
这种情况等下,我们的流程分为三个分支:(相等、小于、大于)。这类不容易死循环,代码如下:
if ( data[mid] == key ) return mid;if ( data[mid] > key ) r = mid-1;else l = mid+1;
2.被查元素不确定,值可能有多个,找到第一个或者最后一个
这是最容易出现死循环的情况,也是本文讨论的核心。这种情况下,流程分成两个分支,我们分两种情况讨论:
a.取第一个小于key的元素:
if ( data[mid] >= key ) r = mid-1;else l = mid;
我们看式子 mid = (l+r)/2
如果(l+r)为奇数,则
mid = (l+r)/2 = (l+r-1)/2 导出 2*mid = (l+r-1)/2*2 = l+r-1
这时,若 mid = l 则“else l = mid;”这句代码就会就会进入死循环。
所以这时使用 mid = (l+r+1)/2 代替 mid = (l+r+1)/2 就不会死循环了。
如果(l+r+1)为偶数,则
mid = (l+r+1)/2 = (l+r)/2 导出 2*mid = (l+r)/2*2 = l+r 不会出现问题。
(这时使用 mid = (l+r)/2 也不会死循环)
综上,这种情况下使用 mid = (l+r+1)/2 就不会死循环了,不过这不是通用式子,看b情况。
int bs( int l, int r, int key ){while ( l < r ) {int mid = (l+r+1)/2;if ( data[mid] >= key )r = mid-1;else l = mid;} return l;}
b.取第一个大于key的元素:
if ( data[mid] <= key ) l = mid+1;else r = mid;
我们看式子 mid = (l+r+1)/2
如果(l+r+1)为奇数,则
mid = (l+r+1)/2 = 导出 2*mid = (l+r+1)/2*2 = l+r+1
这时,若 mid = r 则“else r = mid;”这句代码就会就会进入死循环。
所以这时要使用 mid = (l+r)/2 代替 mid = (l+r+1)/2 才不会死循环了。
如果(l+r)为偶数,则
mid = (l+r)/2 导出 2*mid = (l+r)/2*2 = l+r不会出现问题。
(这时使用 mid = (l+r+1)/2 也不会死循环)
综上,这种情况下使用 mid = (l+r)/2 就不会死循环了。
int bs( int l, int r, int key ){while ( l < r ) {int mid = (l+r)/2;if ( data[mid] <= key )l = mid+1;else r = mid;} return r;}
c.综合a、b得到结论取中值的计算方式与判断条件有关,下面加入一个小优化。
3.一步小优化,防止溢出
这里使用 mid = l+(r-l)/2 代替 mid = (l+r)/2 以及 mid = l+(r-l+1)/2 代替 mid = (l+r+1)/2。这样可以防止l+r和l+r+1溢出。下面证明两者的等价性。
a.l+r为奇数,则r-l为奇数,r-l+1为偶数
mid = l+(r-l+1)/2 = l*2/2 + (r-l+1)/2 = (l+r+1)/2
mid = l+(r-l)/2 = l*2/2 + (r-l-1)/2 = (r+l-1)/2 = (r+l)/2
b.l+r为偶数,则r-l为偶数,r-l+1为奇数
mid = l+(r-l+1)/2 = l*2/2 + (r-l)/2 =(l+r)/2 = (l+r+1)/2
mid = l+(r-l)/2 = l*2/2 + (r-l)/2 = (l+r)/2
c.综上所述上述替代成立。
- 二分查找中的死循环
- 快速查找Unity中的死循环
- 二分查找的越界和死循环问题
- 二分查找中的循环不变式
- 快速查找Unity死循环
- 传说中的死循环:
- 汇编中的死循环
- 死循环中的cin
- 二分查找,偏移循环二分查找
- 又是二分(避免死循环)
- Java中的黑色“死循环”
- 定位java中的死循环
- 聊一聊CSS中的"死循环"
- 查找死循环-飞行记录仪的使用
- 二分查找(while循环)
- 二分查找和循环数组查找
- C++中的二分查找
- 传说中的二分查找
- arm--ldm、stm指令解析
- 早该知道的7个JavaScript技巧
- android listview addHeaderView入参不同的区别
- 图文介绍如何在Eclipse统计代码行数
- Search Insert Position
- 二分查找中的死循环
- 数据结构 第一章 绪论
- 归并排序
- 软工——详解白盒测试
- 哲学家吃饭
- gc overhead limit exceeded eclipse解决方案
- 使用queryperf对DNS服务器作压力测试
- C语言-ATM机
- 欢迎光临