Colin’s Blog

A C++ Programmer

二分总结

## Leetcode 704 二分查找target

这种是在一个元素各不相同的有序数组里面找一个等于target的数 解法:

 1class Solution
 2{
 3public:
 4    int search(vector<int> &nums, int target)
 5    {
 6        int l{0};
 7        int r = nums.size() - 1;
 8        int mid;
 9        while (l <= r)
10        {
11            mid = (l + (r - l) / 2);
12            if (nums[mid] == target)
13            {
14                return mid;
15            }
16            if (target < nums[mid])
17            {
18                r = mid - 1;
19            }
20            else
21            {
22                l = mid + 1;
23            }
24        }
25        return -1;
26    }
27};

此时,分三种情况。注意到,在不等于的时候,r和l都对mid有排斥

排斥性

在二分问题中,mid能不能等于l或者r称为排斥性

二分模版

常见的二分情景是: 对于01数组,找到里面的第一个1出现的位置(重点:这里我们认为1一定会出现,后面的部分会讨论1有可能不出现的情况) 00011111 这符合自然界的规律,某个实验,在参数小于某个阈值的时候,都是失败的。大于等于这个阈值的时候,都是成功的。

这种时候需要我们认真考虑排斥性 对于先0后1,一定会出现1的实验,代码为:

 1vector<int> v{0, 0, 0, 1, 1, 1};
 2    auto yes = [&](int mid)
 3    { return v[mid] == 1; };
 4    size_t l{0}, r{v.size() - 1};
 5    int mid;
 6    while (l < r)
 7    {
 8        mid = l + (r - l) / 2;
 9        if (yes(mid))
10            r = mid;
11        else
12            l = mid + 1;
13    }
14    cout << l;

原因是: 我们之所以设置 while(l<r)因为我们实际上是弄了一个闭区间 [l,r] 规定真正的答案只能出现在这个闭区间里面 也就是说区间定义为:答案可能出现的位置 这样,当我们把l和r缩减到重合的时候,答案就被我们抓住了。

缩减的方法:注意如果mid取了1,那么它可能是答案,也可能在答案的右边,因此设置 r=mid.但如果mid取0,那么答案一定在mid右边,那么设置 l=mid+1

有可能找不到答案的情况

注意到:按照我们上面闭区间的思考模式,一开始就默认了答案在 [l,r]里面 如果采用 [,)左闭右开的方式,也许有别的处理方法。 但这里我的处理是 加入

1if(yes(l))return l;
2else return -1;

这虽然丑陋,但是有效。

例如

 1int main()
 2{
 3    vector<int> v{0, 0, 0};
 4    auto yes = [&](int mid)
 5    { return v[mid] == 1; };
 6    size_t l{0}, r{v.size() - 1};
 7    int mid;
 8    while (l < r)
 9    {
10        mid = l + (r - l) / 2;
11        if (yes(mid))
12            r = mid;
13        else
14            l = mid + 1;
15    }
16    if (yes(l))
17        cout << l;
18    else
19        cout << -1;
20}

确实有效

LOWER_BOUND 和 UPPER_BOUND

对于v={0,1,2,3} lower_bound(v,2)得到2 upper_bound(v,2)得到3

对于 1,1,2,2,2,3,3,3 l(v,2)=2 u(v,2)=5

l(v,1)=0 r(v,1)=2

l(v,0)=0 r(v,0)=0

l(v,3)=5 r(v,3)=6

l(v,4)=6 r(v,4)=6

对于1,1,3,3,5,5,7,7

lower_bound(4)=4 upper (4) =4


lower_bound: Returns an iterator pointing to the first element in the range [first, last) that is not less than (i.e. greater or equal to) value, or last if no such element is found.

upper_bound: Returns an iterator pointing to the first element in the range [first, last) that is greater than value, or last if no such element is found.

区别在于lower_bound是大于等于的第一个 upper_bound是大于的第一个

equal_range

返回 pair<Itr,Itr> 第一个指向区域里面第一个等于val的元素,第二个指向区域里面第一个大于val的元素 也就是左闭右开的 [,)区间