线段树入门(一)
来源:互联网 发布:通货膨胀率 知乎 编辑:程序博客网 时间:2024/06/05 14:13
接触线段树前我们先看一道比较经典的题目。
HDU 1166 敌兵布阵
这道题要求我们对一个区间内的单个值进行修改,并查询一段区间的和。
对于修改,我们很容易暴力的修改,复杂度为O(1),但查询的复杂度就变成了O(n),总复杂度为O(n*m),由于n和m都很大,所以这样肯定会超时的。
对于区间和我们常见的操作有前缀和,用前缀和可以将查询操作的复杂度降为O(1),但修改操作的复杂度则会升到O(n)。总复杂度也没有改变。
当这两种方法都无法满足题目的需求时,我们可以考虑是否可以将修改和查询的复杂度均摊一下,使总复杂度降低。
线段树解决这个问题比较好的工具。
什么是线段树
线段树是一种用树形来维护线性数据的一种数据结构。
一 般的线段树上的每一个节点T[a , b],代表该节点维护了原数列[ a , b ]区间的信息。对于每一个节点他至少有三个信息:左端点,右端点,我们需要维护的信息(在本题中我们维护区间和)。
struct segtree{ int l,r;//左右端点 long long sum;//要维护的信息}tree[maxn<<2];//一般开4*n防止爆空间
建立线段树
由于线段树是一个二叉树,而且是一个平衡二叉树,如果当前结点的编号是i,左端点为L ,右端点为 R , 那么左儿子的 编号为 i*2 ,左端点为 L ,右端点为 (L + R)/2 ; 同理右儿子的 编号为 i*2+1,左端点为(L+R)/2 ,右端点为 R)。如果当前结点的左端点等于右端点,那么该节点就是叶子节点,直接在该节点赋值即可。显然线段树是递归定义的。
void build(int root,int l,int r){ tree[root].l = l;tree[root].r = r;//初始化左右端点 if(l==r)//为叶子节点,直接赋值 tree[root].sum = a[l]; else { int mid = (l+r)>>1; build(root<<1,l,mid);//建立左子树 build(root<<1|1,mid+1,r);//右子树 push_up(root);//从下往上维护线段树 }}
从下往上更新线段树
void push_up(int x){ tree[x].sum = tree[x<<1].sum + tree[x<<1|1].sum;//因题目不同而不同}
单点更新
单点更新的方法很好理解,如果目标更新节点在左儿子里,去左儿子中查找;反之,在右儿子中。不断递归,知道找到需要维护的节点,更新它,然后从下往上维护线段树回来。这就是维护的过程,代码如下:
void update(int x,int q,long long val){ int L=tree[x].l,R=tree[x].r; if(L==q&&R==q)//叶子节点,直接更新 tree[x].num = val; int mid = (L+R)>>1; if(mid>=q) update(x<<1,l,r,val);//在左子树中 if(q>mid) update(x<<1|1,l,r,val);//在右子树中 push_up(x);//从下往上维护线段树}
区间查询
题目中让我们查询区间求和,不难想到如果当前结点的区间完全被目标区间包含,直接返回当前结点的sum值,否则判断是否在左子树,右子树中,然后分别对左子树和右子树进行查询。具体过程通过以下代码理解:
long long query(int x,int l,int r){ int L=tree[x].l,R=tree[x].r; if(l<=L&&R<=r)//完全在查询区间内 return tree[x].sum;//直接返回值 else { long long ans = 0; int mid = (L+R)>>1; if(mid>=l) ans += query(x<<1,l,r);//在左子树中有一部分 if(r>mid) ans += query(x<<1|1,l,r);//在右子树中有一部分 return ans;//返回答案 }}
这样我们就解决了这道题了
- 线段树入门(一)
- 线段树入门(一)
- (一)线段树入门--区间最值查询
- (一)线段树入门--补充与其他模板
- 线段树总结(一) 入门篇
- 线段树(一)
- 线段树(一)
- 线段树入门(1)
- 线段树入门(转!!!!)
- 线段树 (入门篇)
- 【线段树】线段树入门
- [线段树]线段树 入门
- 线段树学习(一)
- 线段树学习(一)
- 线段树(一)HDU1166
- 数据结构-----线段树(一)
- 【线段树】线段树入门之入门
- 【线段树】线段树入门之入门
- iBatis学习笔记
- netty入门学习
- 网络访问协议
- jquery实现图片上传前本地预览功能
- Linux yum 下载rpm包而不进行安装
- 线段树入门(一)
- Appium+Python 自动化-appium常用元素定位方法
- RecyclerView滚动事件分析
- rados swift copy格式
- phpMyAdmin 尝试连接到 MySQL 服务器,但服务器拒绝连接。
- node.js 教你写爬虫(附上gbk,gb2312中文乱码的解决方法)
- CSU-ACM2017暑假集训2-二分搜索 hdu2119
- 部落冲突
- 数理逻辑2 -- 量化理论3