关于二分
找一个值
int find(const int &val, const std::vector<int> &data) {
int l = 0, r = data.size();
while(l < r) {
int mid = (l + r) >> 1;
if (data[mid] == val) {
return mid;
} else if (data[mid] < val) {
l = mid + 1;
} else {
r = mid;
}
}
return -1;
}
int main() {
std::vector<int> data;
for (int i = 0; i < 10; ++i) {
data.emplace_back(rand());
}
std::sort(data.begin(), data.end());
for (auto &x: data) {
std::cout << x << " ";
}
std::cout << '\n';
std::cout << find(data[4], data);
}
或者 find 函数也可以写成
int find(const int &val, const std::vector<int> &data) {
int l = 0, r = data.size() - 1;
while(l <= r) {
int mid = (l + r) >> 1;
if (data[mid] == val) {
return mid;
} else if (data[mid] < val) {
l = mid + 1;
} else {
r = mid - 1;
}
}
return -1;
}
会发现区别在于 while 终止的条件,以及初始的区间右端点是封闭的还是开的,以及更新的时候 r 该怎么更新;\([l, r)\)相比\([l, r]\),不能取到右端点,因此 mid 一定不能取到初始的 r,所以是 <,其次每次都保证下一轮的区间也是一个左闭右开的。
找界
下界
找大于等 k 的最小值
点击查看代码
void func(int idx);
int find(vector<int> &a, const int target) {
int l = 0, r = a.size();
while (l < r) {
int mid = (l + r) >> 1;
if (func(mid) >= target)
r = mid;
else
l = mid + 1;
}
return a[r];
}
上界
找小于等于 k 的最大值
点击查看代码
void func(int idx);
int find(vector<int> &a, const int target) {
int l = 0, r = a.size();
while (l < r) {
int mid = (l + r + 1) >> 1;
if (func(mid) <= target)
r = mid;
else
l = mid - 1;
}
return a[r];
}
为什么上面求界两种写法不一样
考虑上面这个问题先看下面的写法。
我其实更喜欢这种写法
点击查看代码
void func(int idx);
int find(vector<int> &a, const int target) {
int l = -1, r = a.size();
while (l + 1 < r) {
int mid = (l + r) >> 1;
if (func(mid) <= target) // or func(mid) >= target it's ok
r = mid;
else
l = mid;
}
// only r is ok, l is not
return a[r];
}
这样就不需要考虑 mid 是否 +1,但是需要注意循环终止时 l 是不满足条件的,所以初始条件 l 需要置为 -1,r 则是 r.size() - 1.
为什么一个向下取整一个向上取整。
考虑找下界,循环会在 l = r 时结束。l = 2, r = 3,且 r = 3 的值满足 target(这里的 >= 可以代表其他任意具有偏序的关系),l = 2 不是,那么 mid = (2 + 3) / 2 = 2,如果向上面那样不管 l 还是 r 都直接把 l 和 r 置为 mid,那么此时,l = mid 会无限循环下去。
上界同理。

浙公网安备 33010602011771号