关于二分

找一个值

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 会无限循环下去。
上界同理。

ref

  1. https://blog.csdn.net/flushhip/article/details/79261608
posted @ 2023-02-23 14:20  o0yo  阅读(22)  评论(0)    收藏  举报