线段树的建立、查询、更新及应用
来源:互联网 发布:js如何控制光标大小 编辑:程序博客网 时间:2024/06/05 17:35
线段树本质上是一棵二叉搜索树(体现在查找阶段),与普通的二叉树不一样,这个二叉树有点特别:用left和right来表示一个区间,即 [left, right],而其值val,则用来表征这个区间的某些特征,例如:区间最大值,区间最小值,区间和;并且容易知道,当left和right相等时,代表叶结点,其值就是某个数组当前位置的值。
一棵区间为[1, 5]上的线段树
线段树在处理某些问题时是非常方便的,一个经典的问题是:给你一个数组{ai},总共n个值(数组从1到n排列),然后给你一个区间 [l, r],对于每次查询,给出区间 [l, r]的和(或者最大值,最小值)
聪明的小伙伴一定会马上想到用前缀和来处理这个问题,是的,前缀和在预处理阶段为o(n)复杂度,而每次查询只需要o(1),对于这种静态问题是十分方便的,但是,如果我们在上面的问题中加上 “每次随机更新数组中的某个值” 这一条件时,维护前缀和的开销其实是十分巨大的,因为对于每次修改,你不得不对前缀和进行o(n)的更新,如果数据量过大,这种方式是肯定不行的。所以,我们采用线段树这一数据结构来解决这个问题
典型的线段树可以采用如下定义:
struct TreeNode{ int val; int left,right;}tree[4*N];
以下为线段树的三个操作,建树,查询,更新
注:代码以求区间和为例
void build(int root,int l,int r)//建树{ tree[root].left=l; tree[root].right=r; if(l==r)//叶结点 { tree[root].val=arr[l]; return; } int mid=(l+r)/2; build(2*root,l,mid);//左子树 build(2*root+1,mid+1,r);//右子树 tree[root].val=tree[2*root].val+tree[2*root+1].val;//求和}
int query(int root,int l,int r)//查询[l,r]的和{ if(tree[root].left==l&&tree[root].right==r)//正好等于该区间 { return tree[root].val; } int mid=(tree[root].left+tree[root].right)/2; if(mid>=r) return query(2*root,l,r);//在左子树查找 else if(mid+1<=l) return query(2*root+1,l,r);//在右子树查找 return query(2*root,l,mid)+query(2*root+1,mid+1,r);//跨区间查找}
void update(int root,int pos,int val)//更新pos位置的值为val{ if(tree[root].left==pos&&tree[root].right==pos) { tree[root].val=val; return; } int mid=(tree[root].left+tree[root].right)/2; if(pos<=mid) update(2*root,pos,val); else update(2*root+1,pos,val); tree[root].val=tree[2*root].val+tree[2*root+1].val;//更新和}
线段树每次查询和更新操作时,其复杂度为O(logn),这对于我们来说,是较为理想的。
应用问题:
第一行给出n,m,分别为数组元素的个数以及请求个数
第二行给出n个数,
随后m行,每行给出一个操作:opt x y
当opt = 1,更新数组中x位置的值为y
当opt = 2,输出[x, y]的区间和
注:下标从1开始
/* test case8 41 2 3 4 5 6 7 81 2 02 1 81 8 02 2 2*//*output340*/
#include <bits/stdc++.h>#define N 1024using namespace std;struct TreeNode{ int val; int left,right;}tree[4*N];int arr[N];void build(int root,int l,int r){ tree[root].left=l; tree[root].right=r; if(l==r) { tree[root].val=arr[l]; return; } int mid=(l+r)/2; build(2*root,l,mid); build(2*root+1,mid+1,r); tree[root].val=tree[2*root].val+tree[2*root+1].val;}int query(int root,int l,int r){ if(tree[root].left==l&&tree[root].right==r) { return tree[root].val; } int mid=(tree[root].left+tree[root].right)/2; if(mid>=r) return query(2*root,l,r); else if(mid+1<=l) return query(2*root+1,l,r); return query(2*root,l,mid)+query(2*root+1,mid+1,r);}void update(int root,int pos,int val){ if(tree[root].left==pos&&tree[root].right==pos) { tree[root].val=val; return; } int mid=(tree[root].left+tree[root].right)/2; if(pos<=mid) update(2*root,pos,val); else update(2*root+1,pos,val); tree[root].val=tree[2*root].val+tree[2*root+1].val;}int main(){ //freopen("2.txt","r",stdin); int n,m; cin>>n>>m; for(int i=1;i<=n;++i) cin>>arr[i]; build(1,1,n); for(int i=0;i<m;++i) { int opt,x,y; cin>>opt>>x>>y; if(opt==1) update(1,x,y); else cout<<query(1,x,y)<<endl; } return 0;}
- 线段树的建立、查询、更新及应用
- 线段树区间最大值查询,单点更新,建立模板
- 线段树的建立、更新、查找
- hdu1394(线段树点更新的应用)
- [模板]线段树的建树、查询、单点更新、区间更新
- 线段树的建立
- 线段树的建立
- 线段树的建立
- 树的区间查询与更新(线段树)
- HDU 4819:单点更新,区间查询的二维线段树
- HDU1166线段树的点更新区间查询
- 线段树的应用及模版
- 树的建立,遍历,及相关应用
- 线段树初步应用(一段一段的更新) HDOJ - 1698
- zoj (单点更新区间查询:线段树)
- HDU1698线段树区间更新,一次查询
- 线段树单点更新和区间查询
- hdu1166(线段树单点更新区间查询)
- Sqrt(x)--LeetCode
- 前端编码规范及 SASS、SCSS和CSS的关系
- 常用js正则表达式
- maven基础学习
- 打招呼
- 线段树的建立、查询、更新及应用
- HDU 5113 Black And White(DFS)
- PAT 1007.素数对猜想(20)
- Ubuntu 下进制自动挂载 Android MTP
- CCF之字符串的匹配
- 功能强大的gdb
- Python缩进坑
- [论文解读] Vote3Deep: Fast Object Detection in 3D Point Clouds Using Efficient CNN
- Spark-submit脚本