关于如何用线段树实现查找区间内第一个小于(大于)某一值x的方法

来源:互联网 发布:mac显示磁盘 编辑:程序博客网 时间:2024/05/21 08:56

目的:用线段树查找一个区间里面第一个小于x的元素

对于这种问题,我们首先可能会想到用集合维护一个单调性然后加一个二分查找,这样做没问题,但是用STL会太慢,用手写的话暂时还不会。

那么如何在没有单调性的时候查找呢,或者说我就是要用线段树呢?
首先有一个粗暴的方法,直接二分,然后调用线段是,时间复杂度(logn)^2,对于大的数据还是不太理想。

那么直接使用线段树可以么?当然是可以的不然我就不写这篇博客了
我们先看线段树的查找方式,最终会被划分成若干个小区间,这些小区间加起来就是最终我们需要的答案,我们可以很轻松地找到我们查找的元素在哪个小区间里面(讨论一下即可)
对于一个小区间,就简单了很多,线段树自己就有二分性质,所以先看左子树可行吗,如果不可行就要右子树,也就自然而然的找到了我们需要的第一个小于x的值,对于小区间左子树是否可行的判断是很快的,直接进入递归然后加上一个判断如果待查找区间已经包含了L~R,那么就直接可以使用minv函数判断是否存在了。

时间复杂度:log2n+log2n(若干个小区间+一次二分),就比那种暴力二分的方法好多啦

这个方法既避免了set,也有更广的应用范围,在单调性分析没那么尽善尽美的情况下,我们依然有一个比较厉害的手段可以实现较复杂的查找,达到正常时间复杂度。

代码示例

int query(int now,int L,int R,int i,int j,int d){    if(L==R)//达到端点,直接返回     {        if(minv[now]<=d)            return L;        else            return -1;    }    if(i<=L && R<=j)//对于已经包含了的情况,直接判断,也是吧*logn变成+logn的关键         if(minv[now]>d)            return -1;    int m=(L+R)>>1;    if(j<=m)        return query(lc[now],L,m,i,j,d);    if(i>=m+1)        return query(rc[now],m+1,R,i,j,d);//正常的筛出小区间     int t1=query(lc[now],L,m,i,j,d);//先左子树     if(t1!=-1)        return t1;    return query(rc[now],m+1,R,i,j,d);//左子树不行才弄右子树 } 

理解:
1、因为查找元素位置,只有在L=R的时候才返回
2、在待查找区间包含L~R的时候,可以直接判断是否有解,有只找这一次,没有及时退出,所以只有一个log2n
3、正常的筛小区间还是按照常规查找进行
4、体现第一个元素,因此先找左子树,再找右子树

还有找大于,找最后一个元素等不再赘述

阅读全文
2 0