线段树及其基本操作

来源:互联网 发布:做项目计划软件 编辑:程序博客网 时间:2024/06/07 11:10

下图是对线段[1,10)建立的一棵线段树基本结构

1.每个节点都是一个[a,b)的区间,根节点代表了整个所要处理的区间

2. 对于每个非叶节点[a,b),令mid = (a + b) / 2;则其左右儿子节点代表的区间为[a,mid),[mid,b)

3.二叉的组织结构

4. 线段树是一个平衡树,树的高度为logN

5. 线段树把区间上的任意一条长度为L的线段都分成不超过2logL条线段的并

6. 任两个结点要么是包含关系要么没有公共部分,不可能部分重叠,每层节点的区间并即为全体区间

存储方式

线段树是一个树形结构,其上的信息都是保存在树的节点中。用结构体的方式建立节点,一个节点的基本结构如下:

struct node

{

    int left,right,mid;

};

其中left 和 right 分别代表该节点所 表示线段的左右端点,即当前节点所表示的线段为 [left ,right) 。而 mid = (left + right) / 2 ,为当前线段的中点。

这只是基本结构,在具体解题中,需要在节点中添加其他数据域保存信息。                              

堆式存储的特点:

1. N的左儿子是 2N

2. N 的右儿子是 2N + 1

3. N 的父亲是 N / 1

线段树的建树操作

结构体数组保存线段树,根节点下标为1。对于非叶节点num,其左右子节点下标分别为2*num和2*num+1。

node seg_tree[3*MAXN];

//由线段树的性质可知,建树需要的空间大概是所需要处理的最长线段的两倍多,所以需要开3倍大小的数组

void make(int l,int r,int num)

{

    seg_tree[num].left = l;

    seg_tree[num].right = r;

    seg_tree[num].mid = (l +r)/2;

    if (l+1!= r) return;

   make(l,seg_tree[num].mid,2*num);

   make(seg_tree[num].mid,r,2*num+1);

}

线段树的插入操作

为了记录节点中的线段是否被完全覆盖过,需要在节点中添加一个数据域cover。若cover为1则表示此条线段已经被完全覆盖过,否则未被覆盖。

插入操作的代码

void insert(int l,int r,int num)

{

//l,r分别为插入当前节点线段的左右端点,num为节点在数组中的编号

   if (seg_tree[num].left ==l&& seg_tree[num].right == r)

   {

//若插入的线段完全覆盖当前所表示的线段

      seg_tree[num].cover = 1;

      return;

   }

   if (r <=seg_tree[num].mid)

//当前节点的左子节点所代表的线段完全包含插入的节点

      insert(l,r,2*num);

   elseif(l>=seg_tree[num].mid)

//当前节点的右子节点所代表的线段完全包含插入的节点

      insert(l,r,2*num+1);

   else {

//插入线段跨越当前节点所表示的中点

      insert(l,seg_tree[num].mid,2*num);

     insert(seg_tree[num].mid,r,2*num+1);

   }

}

线段树的删除操作

一样采用递归的方法对线段进行删除,如果当前节点所代表的线段未被覆盖,则递归进入此节点的左右子节点进行删除。否则要考虑两种情况。一是删除的线段完全覆盖当前节点所代表的线段,则将当前节点的cover值置0。应该递归的在当前节点的子树上所有节点删除线段。另一种情况是删除的线段未完全覆盖当前节点所代表的线段,通常采用的方法是,将当前节点的cover置0,并将其左右子节点的cover置1,然后递归的进入左右子节点进行删除。

删除操作的代码

void del(intl,int r,int num)

{

    if (seg_tree[num].left ==l&&seg_tree[num].right == r)

    {

        seg_tree[num].cover = 0;

        return;

    }

    if (seg_tree[num].cover)

    {

        seg_tree[num*2].cover = 1;

        seg_tree[num*2 + 1].cover = 1;

        seg_tree[num] = 0;

    }

    if (r <= seg_tree[num].mid)

        del(l,r,2*num);

    else if (l>=seg_tree[num].mid)

        del(l,r,2*num+1);

    else {

         del(l,seg_tree[num].mid,2*num);

         del(seg_tree[num].mid,r,2*num+1);

    }

}

 线段树的统计操作

对应不同的问题,线段树会统计不同的数据,比如线段覆盖的长度,线段覆盖连续区间的个数等等,其实现思路不尽相同。

一般来说,统计都是在区间中进行的,所以依然需要采用递归的方式进行统计。

int cal(int num)

{

   if (seg_tree[num].cover)

     return seg_tree[num].right -seg_tree[num].left + 1;

   if (seg_tree[num].left + 1 == seg_tree[num].right)

     return 0;

   return cal(2*num) + cal(2*num+1);

}

                                                                                                                                                                                            ——本文摘自陈宇老师将要出版的算法图书,转载请标明出处

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 二胎快生了老大特别粘人怎么办 生二胎不舍得大宝跟奶奶睡怎么办 怀二胎婆婆不帮忙带孩子怎么办 注册过的高铁用户名忘了怎么办 硕士延期毕业找好的工作怎么办 竞彩足球绑定信用卡提不了现怎么办 qq启动出现问题请卸载重装怎么办 u盘有文件打开后却是空的怎么办 王者荣耀不记得所在的区服怎么办 交易猫出售游戏账号是微信号怎么办 网银密码输错3次怎么办 无线网卡信号很好就是没网速怎么办 红米2a忘了登陆账号怎么办 qq封了密保手机没用了怎么办 乐视手机重置账号密码忘了怎么办 此版本的ios不支持银联怎么办 单反m档拍出来照片是黑色怎么办 从兴趣部落老发骚扰信息怎么办 在厂里辞一个月厂长不批怎么办 在厂里做管理被员工恐吓怎么办 在葡京娱乐输了很多钱怎么办 从珠海入镜澳门北京往返签注怎么办 艾艾灸灸了一身小子子怎么办? 微信视频已过期或已清理怎么办 视频已过期或已被清理怎么办 小孩作业不会老婆天天吵骂打怎么办 苹果手机在太阳下屏幕变暗怎么办 斗鱼的鱼丸竞猜主播结算了怎么办 附近有个小姐姐想加她好友怎么办 孩子出现听别人说话语速很快怎么办 苹果手机上的邮件删了怎么办 手机qq邮箱独立密码忘记了怎么办 哺乳期吃了人参回奶了怎么办? 扣扣邮箱里的邮件过期了怎么办 一体机的管理员账号被删除了怎么办 手机里的邮箱重要吗删除了怎么办 华为荣耀10账号邮箱忘记了怎么办 大陆微信号在台湾登录不上怎么办 威纶触摸屏被禁止到反编译了怎么办 微信公众号邮箱被占用怎么办 京东绑定的手机号不用了怎么办