标签:one 判断 简单 奇数 搜索 相等 while 最小 缩小
二分搜索?莫非就是对于一个值单调递增的序列,给出\(l,r\),将要找的值与区间\([l,r]\)的中点值\(a[m]\)比较,大了\(l\)变成\(m\),小了\(r\)变成\(m\)呗?但是如果不细究,很容易发生off one错误。下面我们将要讨论该问题。
int Bsearch(int l, int r, int key, int *a)
{
while(l <= r)
{
int mid = (l + r) >> 1;
if (a[mid] == key)
return mid;
if (key < a[mid])
l = mid + 1;
else
r = mid - 1;
}
return -1;
}
因为没有重复数字,所以如果mid的值就是所求,返回mid就可以,否则既然mid的值无效了,范围缩小后的区间就不包括mid了。
int LowerBound(int l, int r, int key, int *a)
{
while(l < r)
{
int mid = (l + r) >> 1;
if (key <= a[mid])
r = mid;
else
l = mid + 1;
}
return l;
}
int UpperBound(int l, int r, int key, int *a)
{
while(l < r)
{
int mid = (l + r + 1) >> 1;
if (key >= a[mid])
l = mid;
else
r = mid - 1;
}
return l;
}
LowerBound:给出\(l,r,x\),通过给出\(p\)的形式,求得一个区间\([p,r]\),使得任意一个下标\(k\),\(k\in [p,r]\Leftrightarrow a_k\geq x\)
UpperBound:给出\(l,r,x\),通过给出\(p\)的形式,求得一个区间\([l,p]\),使得任意一个下标\(k\),\(k\in [l,p]\Leftrightarrow a_k\leq x\)。
\(m=\frac{l+r}{2}\)
左区间:\([l,\lfloor m\rfloor]\),右区间:\([\lceil m\rceil,r]\)。
在LowerBound代码中,mid为\(\lfloor m\rfloor\);在UpperBound代码中,mid为\(\lceil m\rceil\)。
看看两个代码中mid的定义,发现无论是UpperBound,还是LowerBound,其缩小区间时,要么缩小到左区间,要么缩小到右区间。
这意味着\(k\)的值可能有多个。
这意味着\(m\notin \mathbb{Z}\)。如果\(a_{\lfloor m\rfloor},a_{\lceil m\rceil}\)中存在不多于1个与\(x\)相等,这简单,若\(x\geq a_{\lceil m\rceil}\),则转移到右区间;若\(x\leq a_{\lfloor m\rfloor}\),则转移到左区间。两个代码都满足这个要求。
如果\(a_{\lfloor m\rfloor},a_{\lceil m\rceil}\)两个值都与\(x\)相等,则因为LowerBound要的\(k\)值最小,所以它要舍弃\(a_{\lceil m\rceil}\),在左区间中寻找\(k\leq\lfloor m\rfloor,a_k=x\),故要求转移到左区间。同理UpperBound要求转移到右区间。
这意味着\(m\in\mathbb{Z}\)。若\(a_m\neq x\),则若\(x<a_m\),则转移到左区间,否则转移到右区间;
若\(a_m=x\),LowerBound要在左区间中寻找\(k\leq\lfloor m\rfloor,a_k=x\),故要求转移到左区间。同理UpperBound要求转移到右区间。
一开始是普通的二分,最后会遇到这种情况:\(m\notin\mathbb{Z}\),\(a_{\lfloor m\rfloor}<x<a_{\lceil m\rceil}\)。如果是LowerBound,因为\(x>a_{\lfloor m\rfloor}\),LowerBound的定义要求所得区间内所有值都要大于\(x\),故要舍弃\(a_{\lfloor m\rfloor}\),故转移到右区间;UpperBound同理转移到了左区间。
我们看到mid的定义以及小于大于等于判断及其操作,都满足上述要求。
标签:one 判断 简单 奇数 搜索 相等 while 最小 缩小
原文地址:https://www.cnblogs.com/headboy2002/p/8977085.html