[线段树]线段树 入门
来源:互联网 发布:淘宝网针织内衣高领 编辑:程序博客网 时间:2024/05/15 02:48
1.定义
1.1树 树是图论中的一个概念,在图论中,树(英语:Tree)是一种无向图(undirected graph),其中任意两个顶点间存在唯一一条路径。或者说,只要没有回路的连通图就是树。
1.2二叉树 二叉树是每个节点最多有两个子树的树结构。通常子树被称作“左子树”(left subtree)和“右子树”(right subtree)。
1.3二叉查找树 (Binary Search Tree)又称二叉搜索树,二叉排序树。若中序遍历这个二叉查找树,就会得到有序递增的序列。
1.4完全二叉树 除最后一层外,每一层上的节点数均达到最大值;在最后一层上只缺少右 边的若干结点。
1.5满二叉树 除最后一层无任何子节点外,每一层上的所有结点都有两个子结点的二叉树。
1.6线段树:线段树的本质是一棵二叉树,不同于其它二叉树,线段树的每一个节点记录的 是一段区间的信息。有人说线段树是一种二叉搜索树,在树结构上线段树的最后一层是 不一定存在的,即使存在也分布不均匀,它们显然不一样,所以说这种说法是错误的。 又有人说线段树是完全二叉树,同样在树结构上他们也不一样。
2.线段树解决什么问题
区间查询 询问某段区间的某些性质(极值,求和,etc)
区间更新 某些操作影响了某段区间(统一加一个值……)
高效解决连续区间的动态查询问题,由于二叉结构的特性,它基本能保持每个操作的复杂度为O(lgN)!
3.代码&思想
3.1节点信息
用一个结构体记录节点信息
struct Tree{ int left,right; //区间的端点 int max,sum; //节点信息,视题目要求而定};
3.2节点的存储结构
我们用一个数组a记录节点,且根节点的下标为1,
对于任一节点a[k],
它的左儿子为a[2*k]
它的右儿子为a[2*k+1]
这样 一维数组即实现了线段树节点的保存。实际上这是一种顺序存储结构,有比较高的空间利用率。同时直接根据索引可以找到某个节点,以及他的左右儿子,也降低了时间复杂性。
3.3建立一棵线段树,并记录原数组信息
建树的过程主要思想是递归构造,如果当前节点记录的区间只有一个值,则直接赋值,否则递归构造左右子树,最后回溯的时候给当前节点赋值
void build(int id,int l,int r){tree[id].left=l; tree[id].right=r;//初始化区间的左右端点if (l==r)//如果左端点等于右端点,换句话说就是区间里只有1个值{tree[id].sum=a[l];//初始化节点信息tree[id].max=a[l];}else{int mid=(l+r)/2;build(id*2,l,mid);//分别递归的建立左半区间和右半区间build(id*2+1,mid+1,r);tree[id].sum=tree[id*2].sum+tree[id*2+1].sum;//初始化节点信息tree[id].max=max(tree[id*2].max,tree[id*2+1].max;}}
3.4更新某个点的数值,并维护相关点的信息
3.4.1单点更新
void update(int id,int pos,int val)//若更新a[pos]的值为val,调用update(1,pos,val)即可 {if (tree[id].left==tree[id].right)//如果找到了节点,就更新此节点{tree[id].sum=tree[id].max=val;}else{int mid=(tree[id].left+tree[id].right)/2;if (pos<=mid) update(id*2,pos,val);//如果要更新的值在左半区间,就递归更新左半区间else update(id*2+1,pos,val);//否则就更新右半区间tree[id].sum=tree[id*2].sum+tree[id*2+1].sum;//更新此节点信息tree[id].max=max(tree[id*2].max,tree[id*2+1].max)}}
3.4.2区间更新
需要用到延迟标记,每个结点新增加一个标记,记录这个结点是否被进行了某种修改操作(这种修改操作会影响其子结点)。对于任意区间的修改,我们先按照查询的方式将其划分成线段树中的结点,然后修改这些结点的信息,并给这些结点标上代表这种修改操作的标记。在修改和查询的时候,如果我们到了一个结点p,并且决定考虑其子结点,那么我们就要看看结点p有没有标记,如果有,就要按照标记修改其子结点的信息,并且给子结点都标上相同的标记,同时消掉p的标记。(优点在于,不用将区间内的所有值都暴力更新,大大提高效率,因此区间更新是最优用的操作)
3.5查询某区间内元素的和或最大值(以总和为例)
主要思想是把所要查询的区间[a,b]划分为线段树上的节点,然后将这些节点代表的区间合并起来得到所需信息
void query(int id,int l,int r)//调用query(1,l,r)即可查询[l,r]区间内元素的总和{if (tree[id].left==l&&tree[id].right==r)//如果找到了此区间,直接返回return tree[id].sum; //询问总和else{int mid=(tree[id].left+tree[id].right)/2;if (r<=mid) return query(id*2,l,r);//如果目标区间在当前左半区间,就递归左半区间else if (l>mid) return query(id*2+1,l,r)//否则就递归右半区间else return query(id*2,l,mid)+query(id*2+1,mid+1,r);如果目标区间别分成了两部分,就分别计算并返回}}
- 【线段树】线段树入门
- [线段树]线段树 入门
- 【线段树】线段树入门之入门
- 【线段树】线段树入门之入门
- 【线段树】线段树入门之入门
- 【线段树】线段树入门之入门
- 线段树学习入门
- 线段树 入门
- HDU1754 线段树入门
- HDU1556 线段树入门
- HDU1166 线段树入门
- 线段树入门【转】
- 线段树入门
- 线段树入门
- 线段树入门( 转 )
- 线段树入门
- 线段树入门知识
- 线段树-入门
- tcpdump
- [leetcode题后感]Jump Game
- [1] 欧拉函数的一种求解方法
- top
- 安装 Ruby环境
- [线段树]线段树 入门
- 杭电2097
- XStream使用Demo
- E-R图中个数限制的表示和理解
- 巧夺天工的kfifo
- 【opencv学习记录】以迭代器方式访问图像像素,统计像素信息存入文件
- NSNumber 数值
- 巧用运营设计让用户毫不犹豫地“买买买”
- 计算某年的第一天是周几