题目

https://www.acwing.com/problem/content/791/

算法思想

本题目是一道经典的整数二分题目,二分要求数组本身具有有序性或者类似于有序的性质,其本质是在左区间或者右区间找到满足某个性质的边界点。如下图所示

左区间

右区间

以该题目为例,数组本身是单调递增的,因此满足二分的前提条件。其次,我们需要找到元素 k 的起始位置和终止位置,对于起始位置来说,需要满足右区间 >= k,每次得到 arr[mid] >= k 时, r = mid,相反 arr[mid] < k 时,l = mid + 1;对于终止位置来说,需要满足左区间 <= k,每次得到 arr[mid] <= k 时,l = mid,相反 arr[mid] > k 时,r = mid - 1。

注意:对于二分来说,循环一定会终止,但是终止之后边界点是否满足我们的需求,需要进行判断。

解法

#include <cstdio>

const int N = 100010;

int arr[N];

int main() {
    int n, q, k, l, r, mid;
    scanf("%d%d", &n, &q);
    
    for(size_t i = 0; i < n; ++i)
        scanf("%d", &arr[i]);
        
    while(q--) {
        scanf("%d", &k);
        l = 0, r = n - 1;
        while(l < r) {
            mid = l + ((r - l) >> 1);
            if(arr[mid] >= k) r = mid;
            else l = mid + 1;
        }
        
        if(arr[l] != k) { puts("-1 -1"); }
        else {
            printf("%d ", l);
            
            l = 0, r = n - 1;
            while(l < r) {
                mid = l + ((r - l + 1) >> 1);
                if(arr[mid] <= k) l = mid;
                else 
                    r = mid - 1;
            }
            
            printf("%d\n", l);
        }
    }
    
    return 0;
}

为什么在左边界的情况下,我们需要 +1,这是因为当 l = r - 1 的时候,mid 向下取整,会等于 l,如果 arr[mid] <= k,就永远不会更新,因此陷入死循环。

复杂度

时间复杂度:log(n),空间复杂度:O(1)。