• 浅谈二维线段树


    一、定义

    二维线段树,即用线段树维护一个矩阵

    有两种实现方式:

    1、原一维线段树的基础上,每一个节点都是一个线段树,代表第二维

    下图是一个4*4矩阵

    2、四分法转化为一维线段树

    两种方法的空间复杂度都是n*n*log^2

    第一种方法单次操作的时间复杂度是log^2,第二种方法最差可以退化到n

    一维线段树的标记思想,在第一种方法中,可以用于二维线段树的第二维,不可以用于二维线段树的第一维

    第二种方法本质上是四叉的一维线段树,

    在此只介绍第一种方法

    二、基本操作

    1、单点修改+矩阵查询

    单次访问一个位置,查询一个矩阵的总访问次数

    访问位置(x,y)

    所有第一维包含x的都包含位置(x,y),访问次数都要加1

    所以从根到找到第一维包含x所经过的所有节点,在里面找到第二维包含y的节点,访问次数加1

    sum[i][j] 表示的是这个节点所包含的矩阵的所有位置的访问次数之和

    第一维:

    void changex(int kx,int l,int r)
    {
        changey(kx,1,1,h);
        if(l==r) return;
        int mid=l+r>>1;
        if(x<=mid) changex(kx<<1,l,mid);
        else changex(kx<<1|1,mid+1,r);
    }

    对于第二维的修改有两种写法

    ①、从根往下找经过的节点 f访问次数全部+1

    因为此时已经确定了第一维包含,从根往下找y,所经过的区间一定包含y

    void changey(int kx,int ky,int l,int r)
    {
        sum[kx][ky]++;
        if(l==r) return;
        int mid=l+r>>1;
        if(y<=mid) changey(kx,ky<<1,l,mid);
        else changey(kx,ky<<1|1,mid+1,r);
    }

    ②、在第二维中找到y,访问次数+1,祖先节点通过左右子节点的合并修改

    void changey(int kx,int ky,int l,int r)
    {
        if(l==r)
        {
            sum[kx][ky]++;
            return;
        }
        int mid=l+r>>1;
        if(y<=mid) changey(kx,ky<<1,l,mid);
        else changey(kx,ky<<1|1,mid+1,r);
        sum[kx][ky]=sum[kx][ky<<1]+sum[kx][ky<<1|1];
    }

    查询:

    查询左上角为(xl,yl),右下角为(xr,yr)的矩阵的所有位置的访问次数之和

    void queryy(int kx,int ky,int l,int r)
    {
        if(l>=yl && r<=yr)
        {
            cnt+=sum[kx][ky];
            return;
        }
        int mid=l+r>>1;
        if(yl<=mid) queryy(kx,ky<<1,l,mid);
        if(yr>mid) queryy(kx,ky<<1|1,mid+1,r);
    }
    
    void queryx(int kx,int l,int r)
    {
        if(l>=xl && r<=xr)
        {
            queryy(kx,1,1,h);
            return;
        }
        int mid=l+r>>1;
        if(xl<=mid) queryx(kx<<1,l,mid);
        if(xr>mid) queryx(kx<<1|1,mid+1,r);
    }

    2、矩阵修改+单点查询

    修改

    访问左上角为(x1,y1),右下角为(x2,y2)的矩阵

    sum[i][j] 表示的是这个节点所代表的矩阵的整体访问次数

    即这个矩阵的每一个位置都会有一个sum[i][j]的访问次数

    void changey(int kx,int ky,int l,int r)
    {
        if(y1<=l&&r<=y2)
        {
            sum[kx][ky]++;
            return;
        }
        int mid=l+r>>1;
        if(y1<=mid) changey(kx,ky<<1,l,mid);
        if(y2>mid) changey(kx,ky<<1|1,mid+1,r);
    }
    void changex(int kx,int l,int r)
    {
        if(x1<=l&&r<=x2)
        {
            changey(kx,1,1,n);
            return;
        }
        int mid=l+r>>1;
        if(x1<=mid) changex(kx<<1,l,mid);
        if(x2>mid) changex(kx<<1|1,mid+1,r);
    }

    查询

    查询位置(x,y)的访问次数

    因为sum[i][j] 表示的是一个矩阵的整体访问次数,所以找(x,y)经过的所有点都要累计其访问次数

    void queryy(int kx,int ky,int l,int r)
    {
        ans+=sum[kx][ky];
        if(l==r) return;
        int mid=ly+ry>>1;
        if(y<=mid) queryy(kx,ky<<1,l,mid);
        else queryy(kx,ky<<1|1,mid+1,r);
    }
    void queryx(int kx,int l,int r)
    {
        queryy(kx,1,1,n);
        if(l==r) return;
        int mid=l+r>>1;
        if(x<=mid) queryx(kx<<1,l,mid);
        else queryx(kx<<1|1,mid+1,r);
    }

    这两个sum[][] 含义不同,是为了适应不同的修改方式

    一个是单点修改,一个是矩阵修改

    而且矩阵修改不带标记下传

  • 相关阅读:
    token是什么?和session什么区别,怎么用
    HashTable详解
    Cookie和Session的区别
    测试基础面试题
    什么是回归测试?回归测试的主要目的是什么?
    每天一个linux常用命令--ls 和 -ll 有什么区别?
    python中6个序列的内置类型分别是什么,列表和元组的异同有哪些
    今天去面试自动化测试,被几个问题问住了,记录下
    python排序算法-冒泡和快速排序,解答阿里面试题
    Myeclipse使用积累
  • 原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/8151375.html
Copyright © 2020-2023  润新知