算法复习|二分查找

适用于有序查找,时间复杂度 O ( l o g n ) O(logn) O(logn)

核心算法

非递归版本

int BinarySearch1(int a[], int value, int n){
    int low=0,high=n-1,mid;
    while (low<=high){ // 终止条件 low>high
        mid=(low+high)/2;
        if(a[mid]==value) // 判断是否找到,可换为其他语句
            return mid;
        else if(a[mid]>value)
            high=mid-1; // 区间中值在目标值右侧,故选择左半边,变更右边界
        else if(a[mid]<value)
            low=mid+1; // 区间中值在目标值左侧,故选择右半边,变更左边界
    }
    return -1; // 没找到
}

递归版本

int BinarySerch2(int a[], int value, int low, int high){
    if(low>high) // 终止条件 low>high
        return -1; // 没找到
    int mid=low+(high-low)/2;
    if(a[mid]==value) // 判断是否找到,可换为其他语句
        return mid;
    else if(a[mid]>value) // 区间中值在目标值右侧,故选择左半边,变更右边界
        return BinarySerch2(a, value, low, mid-1); 
    else if(a[mid]<value) // 区间中值在目标值左侧,故选择右半边,变更左边界
        return BinarySerch2(a, value, mid+1,high);
}

STL中的二分查找函数

lower_bound(start, end, value, compare)
upper_bound(start, end, value, compare)
binary_search(start, end, value,copmare)

lower_bound的作用是在不减的数组中进行二分检索,找到大于等于value的第一个地址,不存在则返回地址end

upper_bound的作用是在不减的数组中进行二分检索,找到大于value的第一个地址,不存在则返回地址end

binary_search的作用是在不减的数组中进行二分检索,判断value在数组中是否存在,存在则返回ture,不存在则返回false

如在数组{1,2,2,2,3}中,取value=2,则:

  • lower_bound返回的地址是第一个2的地址
  • upper_bound返回的地址是**3的地址**

参数解释

start 参与查找的数组首地址,一般为数组名

end 参与查找的数组尾地址+1,一般为数组名+数组长度

value 目标值

compare 自定义的查找规则(比较函数),默认为上述规则。

对于递减数列,设置compare参数为greater<type>(),可以实现查找“小于等于value”和“小于value”的功能,如在数组{3,2,2,2,1}中,取value=2,则:

  • lower_bound(start, end, value, greater<int>())返回第一个2的地址
  • upper_bound(start, end, value, greater<int>())返回**1的地址**

使用

由于函数返回的是地址,一般通过减去数组首地址的方式来获取目标的下标,如下:

int loc=lower_bound(a, a+len, value)-a; // 得到value对应的下标

例题

POJ-2456 Aggressive cows

中文版题目地址:天鹅棚

在中国传媒大学的钢琴湖里有 C C C只天鹅,XH0822为它们建造了一座包括 N N N个隔间的天鹅棚,分别在坐标轴上的 x 1 , ⋯   , x N x_1,\cdots ,x_N x1,,xN位置。但这些天鹅都彼此看不惯对方,为了防止他们互相伤害,所以不能把几只天鹅放在一个隔间里,而且还应该使两只天鹅之间的最小距离尽可能的大。可XH0822思考了好久,都不知道这个最大的最小距离是多少,你能不能帮帮他呢?

数据范围: 2 ≤ N ≤ 100000 , 0 ≤ x i ≤ 1 0 9 , 2 ≤ C ≤ N 2\leq N \leq 100000, 0 \leq x_i \leq 10^9, 2 \leq C \leq N 2N100000,0xi109,2CN

输入

有多组测试数据,以EOF结束
第一行包含两个整数 N N N C C C
后面接着有 N N N行,分别表示 x i x_i xi的位置。(因为输入流可能很多,建议用scanf读取数据)

输出

每组测试数据输出一个整数,即题目中所说的最大的最小值。

输入样例

5 3
1
2
8
4
9

输出样例

3

参考思路及代码

二分枚举所有可能的距离 d d d

  • 距离的范围应为 0 ≤ d ≤ 所有隔间中距离最远的两个之间的距离 0 \leq d \leq \text{所有隔间中距离最远的两个之间的距离} 0d所有隔间中距离最远的两个之间的距离——明确区间的原始上下限
  • 对于每个 d d d计算此时可以放鹅的房间数 n u m num num,并与题中要求的 C C C比较,如果num>=C,则说明当前的 d d d是满足条件的,我们去找有没有更大的 d d d也满足条件,即变更区间下限low=mid+1,反之则要缩小距离再次尝试,即变更区间上限high=mid-1——确定判断规则
  • 题目中给出的数组 x x x是无序的,需要先排序,以便枚举时的验证
#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
const int maxn=100020;
int x[maxn];
int C,N;
bool isEnough(int distence){
    int num=1,separate_loc=0;
    for(int i=1;i<N;i++)
        if(x[i]>=x[separate_loc]+distence){
            num++;
            separate_loc=i;
        }
    if(num>=C)return 1;
    return 0;
}
int BinarySearch(){
    int low=0,high=x[N-1]-x[0],mid;
    while (low<=high){
        mid=(low+high)/2;
        if(isEnough(mid))low=mid+1;
        else high=mid-1;
    }
    //return low-1;
    return high;

}
int main(){
    cin>>N>>C;
    for(int i=0;i<N;i++)
        scanf("%d",&x[i]);
    sort(x,x+N);
    printf("%d",BinarySearch());
    return 0;
}