[分治] lower/upper bound(非STL)

lower/upper bound

  有些时候我们需要知道有序数列中第一个大于或等于k(lower bound)的数或者第一个大于k的数(upper bound),如果我们一个一个查找时间复杂度是O(n)的,在STL(Algorithm库)中有两个函数lower_bound,和upper_bound,函数支持以上操作,为了更好地理解以上函数我们将手写上述函数并讲解它的工作原理.

[算法描述]

  我们假设我们所需要查找的数组是a[]对于每个函数我们都设置3个变量,第一个是左端点,第二个是右端点,第三个是需要查找的k,边界都是当前查找的区间(left,right)中都有left<right否则不再继续查询.对于lower bound(查找第一个大于或等于k的数)而言,我们每次查找中间的一个数,这个数大于或等于k时我们将right赋值为mid,否则就把left赋值为mid+1(因为此时mid不是解所以我们可以直接从mid+1到right查找下一个解),那么现在有两种情况(1)区间内有解,这样我们找出来的left保存的就是解直接返回left的值即可,(2)区间内无解,此时left=right=r,这时我们规定如果返回r+1即无解所以这时返回left+1即可(其实等价与r+1)(注意由于有可能最后答案为r时可能正好是唯一解,为了解决这个问题我们最后判断一下当前改点是否小于k如果小于k执行上述操作即可)(当然也可以一开始就判断一下最后一个数和第一个数试一下是否有解).upper bound 类似,我们在二分的时候把mid>k时把right赋值为mid(因为我们要找的是第一个大于k数值所以这里并不能等于),其他和lower bound类似.注以上返回的其实是a[]中对应数值所在下标(STL中返回的是迭代器位置).

[代码(lower bound)]

inline int lower_bound(int l,int r,int k){    //返回在数组a中第一个大于等于k的下标
    int left=l,right=r,mid=0;
    while(left<right){    //设置边界
        mid=left+(right-left)/2;    //取终点
        if(a[mid]>=k){    //由于我们要找的是第一个大于等于k的下标
            //所以中点如果大于或等于k,我们就在(left,mid)中查找是否有一个更小答案
            right=mid;
        }else{
            //如果中点小于k,我们就在(mid+1,right)中查找是否具有答案
            left=mid+1;
        }
    }
    //由于我们找的是区间(l,r)之间如果中间不存在解,那么就输出r+1(经过以上操作后left=r)
    if(a[left]<k){    //这里有且只有在无解的时候才会++
        left++;
    }
    return left;
}

[代码(upper_bound)]

inline int upper_bound(int l,int r,int k){    //返回在数值a中第一个大于k的下标
    int left=l,right=r,mid=0;
    while(left<right){    //设置边界
        mid=left+(right-left)/2;    //取中点
        if(a[mid]>k){    //由于我们要找的是第一个大于k的下标
            //所以中点如果大于k就在(left,mid)中寻找
            right=mid;
        }else{
            //如果中点小于k,我们就在(mid+1,right)中寻找是否有答案
            left=mid+1;
        }
    }
    if(a[left]<=k){    //只有无解时才会++
        left++;
    }
    return left;
} 

[代码(调用即操作示范)]

/*
    Name: Lower/Upper Bound
    Author: FZSZ-LinHua
    Date: 2018 06 13
    Time complexity: O(log n)
    Algorithm: Divide-and-conquer
*/
# include "iostream"
# include "cstdio"

const int maxm=10000+10; 

int n,m,a[maxm],k; 

inline int lower_bound(int l,int r,int k){    //返回在数组a中第一个大于等于k的下标
    int left=l,right=r,mid=0;
    while(left<right){    //设置边界
        mid=left+(right-left)/2;    //取终点
        if(a[mid]>=k){    //由于我们要找的是第一个大于等于k的下标
            //所以中点如果大于或等于k,我们就在(left,mid)中查找是否有一个更小答案
            right=mid;
        }else{
            //如果中点小于k,我们就在(mid+1,right)中查找是否具有答案
            left=mid+1;
        }
    }
    //由于我们找的是区间(l,r)之间如果中间不存在解,那么就输出r+1(经过以上操作后left=r)
    if(a[left]<k){    //这里有且只有在无解的时候才会++
        left++;
    }
    return left;
}

inline int upper_bound(int l,int r,int k){    //返回在数值a中第一个大于k的下标
    int left=l,right=r,mid=0;
    while(left<right){    //设置边界
        mid=left+(right-left)/2;    //取中点
        if(a[mid]>k){    //由于我们要找的是第一个大于k的下标
            //所以中点如果大于k就在(left,mid)中寻找
            right=mid;
        }else{
            //如果中点小于k,我们就在(mid+1,right)中寻找是否有答案
            left=mid+1;
        }
    }
    if(a[left]<=k){    //只有无解时才会++
        left++;
    }
    return left;
} 

int main(){
    scanf("%d%d",&n,&m);    //n个数存入a[]中,m次操作
    register int i;
    int x;
    for(i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    for(i=1;i<=m;i++){
        scanf("%d",&x);    //需要查询的是x
        printf("%d\n",lower_bound(1,n,x));    //在整个数组中查找(这里只返回下标无论是否有解)
    }
    return 0;
} 

原文地址:https://www.cnblogs.com/FJ-LinHua/p/9180857.html

时间: 2024-11-05 19:34:04

[分治] lower/upper bound(非STL)的相关文章

二分查找里的upper bound与lower bound的实现与分析

1. 问题引入 最近参选了学堂在线的课程数据结构(2015秋).课程由清华大学的邓俊辉老师主讲,在完成课后作业时,遇到了这样一个题目范围查询.在这个题目中,我需要解决这样一个子问题:给定了一组已经排好序的整数集合A[0...n]和一组闭区间[L,R],求这个整数集合中落在这个区间中的点的个数.解决这个问题,我们很容易想到查找效率很高的二分查找,但是这又不是一般求key是否在一个数组里面的二分查找问题.对于区间左端点L,要找到数组里面大于或等于它的最小的元素的下标indexL.对于区间右端点R,要

PKU2018校赛 H题 Safe Upper Bound

http://poj.openjudge.cn/practice/C18H 题目 算平均数用到公式\[\bar{x}=\frac{x_1+x_2+x_3+\cdots+x_n}{n}\] 但如果用int型计算,那么\(x_1+x_2+x_3+\cdots+x_n\)可能会超过\(2^{31}-1\) 算6个数的平均数可以这么算 Calculate the average of\(x_1,x_2,x_3\)\[\bar{x}_1=\frac{x_1+x_2+x_3}{3}\]Calculate t

字符串类型1、strip,lstrip,rstrip 2、lower,upper 3、startswith,endswith 4、format的三种玩法 5、split,rsplit 6、join 7、replace8、isdigit # 判断

# name="11YAng11xin11"# strip# print(name.strip("1"))#去掉两边相同的字符# print(name.lstrip("1"))#l:表示左,在strip加l等于去掉左边的字符# print(name.rstrip("1"))#r:表示右,在strip加r等于去掉右边的字符 # lower,upper# name="11YAng11xin11"# print(

my understanding of (lower bound,upper bound) binary search, in C++, thanks to two post

thanks to A simple CPP solution with lower_bound and C++ O(logn) Binary Search that handles duplicate, thanks to phu1ku 's answer on the second post. http://en.cppreference.com/w/cpp/algorithm/upper_bound Returns an iterator pointing to the first ele

oracle字符函数lower() upper() length() substr()

一,字符函数: 小写函数:lower(): 用法:比如将一个表的所有名称都小写: select lower(t.ename) from scott.emp t 大写函数:upper(): 用法:比如将一个表的所有名称都大写: select upper(t.ename) from scott.emp t 长度函数:length(): 用法:比如将一个表的所有名称中为5个字符长度的取出: select t.ename from scott.emp t where length(t.ename)= 5

mysql lower,upper实现大小写

mysql的lower和uppper函数可以将指定字符串转换为小写和大写 select lower('OutSpringTd') as lowerCase, upper('OutSpringTd') as upperCase; 将输出: outspringid OUTSPRINGID  

Oracle lower(Upper)

ower select lower(user_name) from user 将 user表里的user_name字段信息中含有字母的全部转成大写的方法: update user set user_name=Upper(user_name) 同理得到将 user表里的user_name字段信息中含有字母的全部转成小写的方法: update user set user_name=lower(user_name)

32 字符串常用的方法 center find join split lower upper

第八课 字符串中常用的方法:center方法 # 字符串方法:center # 作用是:将字符串在一定的宽度区域内居中显示 # 这个方法和我们之前将的format 中的 ^ 一样 # ^ print("<" + "hello".center(30) + ">") # < hello > # < hello > print("<{:^30}>".format("hello

calc 多项式计算 (STL版和非STL版) -SilverN

计算(calc.cpp) [问题描述] 小明在你的帮助下,破密了Ferrari设的密码门,正要往前走,突然又出现了一个密码门,门上有一个算式,其中只有“(”,“)”,“0-9”,“+”,“-”,“*”,“/”,“^”求出的值就是密码.小明数学学得不好,还需你帮他的忙.(“/”用整数除法) [输入] 输入文件calc.in共1行,为一个算式. [输出] 输出文件calc.out共1行,就是密码. [输入样例]calc.in 1+(3+2)*(7^2+6*9)/(2) [输出样例]calc.out