Acwing.789.数的范围

188 阅读1分钟

www.acwing.com/problem/con…

本题的考点在于二分查找。二分查找的主要思想就是,针对一个有序的数据集合,每次都通过跟区间的中间元素对比,将待查找的区间缩小为之前的一半,直到找到要查找的元素,或者区间被缩小为0。

举个例子:

对于数组[8, 11, 19, 23, 27, 33, 45, 55, 67, 98],查找元素19

除此之外,还有类似于查找最后一个值等于给定值的元素,查找第一个大于等于给定值的元素,查找最后一个小于等于给定值的元素。

这里推荐两个二分查找的模板,掌握之后可以很轻易地解决二分的问题: www.acwing.com/blog/conten…

针对二分查找,我这里需要重点强调一点,就是其本质并不是单调性,而是找到满足某种性质的边界。例如下图所示,对于一个数据集合来说,红线部分和绿线部分都满足某种独特的性质,那么通过二分,我们就可以找到这两部分的边界。

#include <cstdio>

const int N = 100010;

int arr[N];

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

对于模板中求解mid时,为什么有时候需要+1?

因为在第一种情况下,i一直在更新,而j则有可能会保持不变,但是前提条件是i < j,所有当i和j相差1的时候,最终还是会落在i上,所以更新会继续,不会出现死循环;而第二种情况下,更新的是j,i有可能会保持不变,当i和j相差1的时候,最终会落在i上,导致死循环的出现,因此我们需要在i和j相差为1的时候,落在j上,这样更新就会继续。

参考 王争 《数据结构与算法之美》