首页 > 编程语言 >区间树上查找所有与给定区间相交的区间-算法复杂度正确性证明

区间树上查找所有与给定区间相交的区间-算法复杂度正确性证明

时间:2023-11-16 22:45:36浏览次数:27  
标签:结点 ch min max 复杂度 TNode 正确性 right 区间

区间树是在平衡树上维护的数据结构,按照左端点大小排序。详见《算法导论》。

算法设计思路

红黑树的拓展

在红黑树上维护结点属性\(min, max\):

\(min\)表示该结点及其所有后代结点中的区间低端的最小值。

\(max\)表示该结点及其所有后代结点中的区间高端的最大值。

在插入时,对结点路上经过的所有结点的\(min,max\)值进行更新。

在插入后的调整时,在rotate函数中对旋转后结点的\(min,max\)值进行更新维护。

查找算法

剪枝:

根据一个结点\(x\)上的\(x.min,x.max\)属性,由\(x.min,x.max\)的定义可以知道:

区间\([a, b]\)与\([x.min, x.max]\)不相交,则\(x\)和\(x\)的所有后代结点都不与\([a, b]\)相交。

证明:假设以\(x\)为根的子树中存在结点\(y\),\(y\)中所包含的\([y.low, y.high]\)区间与\([a, b]\)相交。则要么\(y.low < b\),要么\(y.high > a\)。则\(x.min \le y.low < b\)或\(x.max >= y.high > a\)。这与\([a, b]\)和\([x.min, x.max]\)不相交矛盾。

此时可以进行剪枝,不探索以\(x\)为根的子树。

查找算法:

根据以上的剪枝原则,对整个区间树进行遍历,要剪枝的结点则不进行遍历。因为剪枝剪掉的子树内不可能有答案,所以这个遍历的算法可以保证输出所有的答案。

下面是对算法时间复杂度的证明:

考虑\([a, b]\)与\([x.min, x.max]\)相交,但\(x\)的子树内无答案的情况。为简便,记满足条件“\([a, b]\)与\([x.min, x.max]\)相交,但\(x\)的子树内无答案“为满足性质A。

首先,\(x.min, x.max\)两个值不会在区间\([a, b]\)内,否则\(x\)子树内必然存在某一结点\(y\),满足\(y.low = x.min\)或者\(y.high = x.max\),由此得出\(y\)的区间与\([a, b]\)相交,与假设矛盾。

又由\(x.min < x.max\),\([a, b]\)与\([x.min, x.max]\)相交两个条件,所以只有一种情况:

\[x.min < a \le b < x.max \]

以上这个性质是性质A的推论。

记\(x\)的两个孩子为$xl \(和\)xr\(,则两个孩子的\)[min, max]\(区间至多有一个与\)[a, b]$相交,以下证明:

如果\(xl.max > b\),则说明在\(xl\)的子树内存在一个结点\(y\),满足\(y.high = xl.max > b\)。因为\(y\)的区间不与$[a, b] \(相交,则必然有\)y.low > b\(。由于区间树的性质,\)xr\(子树中的任意一点\)z\(都有\)z.low > y.low > b\(,则必然有\)xr.min > b$。

即,如果\(xl\)的\([xl.min, xl.max]\)与\([a, b]\)相交,则\(xr\)的就不可能与\([a, b]\)相交。

这个结论的逆否命题也是正确的:如果\(xr\)的\([xr.min, xr.max]\)与\([a, b]\)相交,则\(xl\)的就不可能与\([a, b]\)相交。

综合这两条,所以两个孩子的\([min, max]\)区间至多有一个与\([a, b]\)相交,而这个孩子也是满足性质A的。

所以,对于\(x\)的子树的搜索,每一层只会向其中一个孩子进行搜索,搜索的结点个数为\(x\)的子树的深度,即\(O(\log n)\)。

最终,算法在\(x\)的子树中的搜索会停在这样一个结点\(z\):z本身满足性质A,\(z\)的左孩子\(zl\)满足\(zl.max < a\),\(z\)的右孩子\(zr\)满足\(zr.min > b\)。

这样的结点\(z\)整棵树只会有一个,下面是证明。

假设存在另一个结点\(z'\),满足:\(z'.min < a \le b < z'.max\),\(z'\)的左孩子\(zl'\)满足\(zl'.max < a\),\(z\)的右孩子\(zr'\)满足\(zr'.min > b\).

首先由\(min,max\)两个属性的性质可以确定,\(z\)和\(z'\)之间,没有一个点为另一个点的祖先的情况。

其次,如果\(z\)在中序遍历中比\(z'\)靠前,则必然有\(zr.min < zl'.min\)。然而,又有\(zr.min > b, zl'.min \le zl'.max < a \le b\)。产生矛盾。

同理,\(z\)在中序遍历中比\(z'\)靠后也会产生矛盾。

综上,\(z\)这样性质的点在树中是唯一的。

对于\(x\)的任意祖先\(g\),有\(g.min \le x.min < a \le b < x.max \le g.max\)。而且存在一个唯一的祖先\(g^*\),满足:\(g^*\)的子树内没有答案,且(\(g^*\)是根节点或\(g^*\)的所有祖先的子树内有答案)。

因为\(z\)的唯一性,同时所有满足性质A的结点都是\(z\)的祖先,所以从\(z\)向祖先方向追溯一定会找到这个\(g^*\),而且\(g^*\)也是唯一的。

综上所述,在搜索过程中,满足性质A的结点在整棵树的搜索过程中最多只会遇到一次,而且在这个结点的子树中会遍历\(O(\log n)\)个结点。

除了如上所述的情况以外,该搜索算法只会遍历集合\(A=\{结点x|x的子树内有答案\}\)中的点。因为集合\(A\)由所有的答案,以及所有答案的祖先构成,所以\(|A|=O(\min(n, k\log n))\)。

在每一个结点上消耗的时间是\(O(1)\)的。

综上所述,搜索的总时间复杂度为:

\[T(n) = O(\min(n, k\log n))+O(\log n)=O(\min(n, k\log n)) \]

算法实现

复制红黑树的实现代码,并对其作出修改。主要的修改是添加对min, max两个属性的维护。

数据结构的定义:

// 定义区间
struct Interval {
    int l, r;
    Interval() {
        l = r = 0;
    }

    Interval(int _l, int _r) {
        l = _l;
        r = _r;
    }
	// 在红黑树中的排序方式
    bool operator < (const Interval &b) const {
        return l < b.l;
    }
};
struct TNode {
    int color;
    Interval key;  // key为区间
    int max, min;  // 新添加的属性
    TNode *left;
    TNode *right;
    TNode *p;
    static const int LEFT = 0, RIGHT = 1;
    static const int RED = 2, BLACK = 3;
} node[MAXN], *root = NULL, *nil = NULL;

实现维护min, max属性的代码,并添加进rotate函数里。

void rotate(TNode *x, int side) {
    TNode *ch, *g, *cou;
    if (side == TNode :: LEFT) {
        ch = x->right, g = x->p, cou = ch->left;
        if (g != nil) {
            if (isLeftCh(x)) {
                g->left = ch;
            } else {
                g->right = ch;
            }
        }
        ch->p = g;

        x->p = ch;
        ch->left = x;
        
        x->right = cou;
        if(cou != nil) cou->p = x;
    } else {
        ch = x->left, g = x->p, cou = ch->right;
        if (g != nil) {
            if (isLeftCh(x)) {
                g->left = ch;
            } else {
                g->right = ch;
            }
        }
        ch->p = g;

        x->p = ch;
        ch->right = x;
        
        x->left = cou;
        if (cou != nil) cou->p = x;
    }
    if (ch->p == nil)
        root = ch;
    update(x); // 更新min,max
    update(ch); // 更新min, max
}

在insert的路上更新min, max

void insert(Interval val) {
    TNode *y = nil;
    TNode *x = root;
    TNode *z = new TNode;
    z->key = val;
    z->max = val.r;
    z->min = val.l;
    z->p = z->left = z->right = nil;
    z->color = TNode :: RED;
    while (x != nil) {
        y = x;
        x->max = max(x->max, z->key.r); // 更新max
        x->min = min(x->min, z->key.l); // 更新min
        if (z->key < x->key) {
            x = x->left;
        } else {
            x = x->right;
        }
    }
    z->p = y;  
    if (y == nil) {
        root = z;
        return ;
    }
    if (z->key < y->key) {
        y->left = z;
    } else {
        y->right = z;
    }
    insertFix(z);
}

搜索算法,使用中序遍历。

bool overlap(Interval a, Interval b) {
    return a.r >= b.l && b.r >= a.l;
}

bool intervalSearch(TNode *p, Interval x) {
    bool flag = false;
    if (p->left != nil && p->left->max >= x.l)
        flag |= intervalSearch(p->left, x);
    if (overlap(p->key, x)) {
        printf("[%d, %d]\n", p->key.l, p->key.r);
        flag = true;
    }
    if (p->right != nil && p->right->min <= x.r) 
        flag |= intervalSearch(p->right, x);
    return flag;
}

标签:结点,ch,min,max,复杂度,TNode,正确性,right,区间
From: https://www.cnblogs.com/lightmain-blog/p/17837446.html

相关文章

  • 空间复杂度
    代码随想录笔记:空间复杂度:对一个算法在运行过程中占用内存空间大小的量度。注意对于与算法无关的空间不算入时间复杂度,例如存储某些输入的数组。不要以为空间复杂度就已经精准的掌握了程序的内存使用大小,很多因素会影响程序真正内存使用大小,例如编译器的内存对齐,编程语言容器的......
  • 由数据范围反推算法复杂度以及算法内容
    由数据范围反推算法复杂度以及算法内容一般ACM或者笔试题的时间限制是1秒或2秒。在这种情况下,\(\mathrm{C}++\)代码中的操作次数控制在\(10^{7}\sim10^{8}\)为最佳。下面给出在不同数据范围下,代码的时间复杂度和算法该如何选择:\(n\leq30\),指数级别,\(\mathrm{dfs......
  • 蓝桥杯管道 -- 二分, 区间覆盖
    蓝桥杯管道--二分,区间覆盖原题链接参照执梗大佬的代码,我太菜了wuwuwu......importjava.util.ArrayList;importjava.util.Collections;importjava.util.List;importjava.util.Scanner;/***ClassName:Main12*Package:*Description:**@author:LH寒酥......
  • 一篇掌握华三企业设备---密码复杂度要求(收藏备用)
    作者:网络之路一天 首发公众号:网络之路博客(ID:NetworkBlog)关于密码复杂度要求实际中有的环境对于密码的复杂度、多久修改密码有要求或者客户不想弄的这么复杂,这个时候就需要来定义密码复杂度要求了。[CoreA]local-useradmin[CoreA-luser-manage-admin]passwordsimpleadminThe......
  • 时间复杂度与空间复杂度分析
    noip模拟赛爆空间真难受。。。。空间常数1Byte=8bit(位)。KB,MB,TB......采用1024进制。short2字节(-215~215)整数型int4字节(-231~231)整数型longlong 8字节(-263~263)整数型unsignedlonglong8字节[0~264)整数型char 1字节(0~256)ascll码......
  • P8317 [FOI2021] 幸运区间
    P8317[FOI2021]幸运区间题目传送门 分治+dfs 首先可以发现\(k\)和\(d\)很小,所以是可以搜索的。 那么就考虑如何枚举区间,显然\(n^2\)枚举是会超时的,所以就考虑分治来求。 求的过程中就分成三种情况来处理:在左边一半,在右边一半,以及跨越中间点。显而易见的是,跨越......
  • 锦城 Week0 Blog 对拍 Debug 复杂度
    Blog我们在学习的时候,需要些一些笔记,把所学记录和整理下来,作为计算机专业的学生,不太可能用纸笔来记笔记,所以我们需要写博客(Blog)众多大佬都有自己的博客美团技术团队Martian148的博客写博客可以帮助我们更好的吸收,消化知识,把自己所学的东西用简单的话语讲出来(费曼学习法)......
  • [链表] 2-链表内指定区间反转
    ----------......
  • Excel区间频率统计
    有时候会使用Excel统计一下分段区间数据的频率,也就是数据在不同的区间的分布情况。下面案例就是使用Excel统计一下数据的区间分布情况。使用frequency函数可以得到想要的结果。公式=frequency(数据列,分界区间),然后CTRL+SHIFT+ENTER注意点:要全部选中要填写的表格,在这里就......
  • 区间DP
    一.定义即对于一个区间进行的dp二.经典转移方程1.枚举断点型f[l][r]=min(f[l][k-1],f[k][r])(l+1<=k<=r)2.左右端点型f[l][r]=min(f[l][r-1],f[l+1][r])3.有一定条件型f[l][r][k]=f[l][r-1][k-1]+f[r][r][0]三.主要思想1.遇到环时,破环为链,转化为区间求解2.区间由短到......