数据结构:线段树 【转】
http://blog.csdn.net/wypblog/article/details/8219727
一、线段树基本概念
线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。
对于线段树中的每一个非叶子节点[a,b],它的左儿子表示的区间为[a,(a+b)/2],右儿子表示的区间为[(a+b)/2+1,b]。因此线段树是平衡二叉树,最后的子节点数目为N,即整个线段区间的长度。
使用线段树可以快速的查找某一个节点在若干条线段中出现的次数,时间复杂度为O(logN)。而未优化的空间复杂度为2N,因此有时需要离散化让空间压缩。
性质:父亲的区间是[a,b],(c=(a+b)/2)左儿子的区间是[a,c],右儿子的区间是[c+1,b],线段树需要的空间为数组大小的四倍
二、线段树的存储数据结构
由上面的图可以看出,存储一颗线段树和二叉树有点类似,需要左孩子和右孩子节点,另外,为了存储每条线段出现的次数,所以一般会加上计数的元素,其具体如下:
1 struct Node // 线段树 2 { 3 int left; 4 int right; 5 int counter; 6 }segTree[4*BORDER];
其中,;left代表左端点、right代表右端点,counter代表每条线段出现的次数,BORDE代表线段端点坐标不超过100。由上面的性质可以知道,我们需要4倍的空间来存储。
三、线段树支持的操作
一颗线段树至少支持以下四个操作:
- void construct(int index, int lef, int rig),构建线段树 根节点开始构建区间[lef,rig]的线段树
- void insert(int index, int start, int end),插入线段[start,end]到线段树, 同时计数区间次数
- int query(int index, int x),查询点x的出现次数,从根节点开始到[x,x]叶子的这条路径中所有点计数相加方为x出现次数
- void delete_ (int c , int d, int index),从线段树中删除线段[c,d]
具体操作如下:
1、线段树的创建

1 /* 构建线段树 根节点开始构建区间[lef,rig]的线段树*/ 2 void construct(int index, int lef, int rig) 3 { 4 segTree[index].left = lef; 5 segTree[index].right = rig; 6 if(lef == rig) // 叶节点 7 { 8 segTree[index].counter = 0; 9 return; 10 } 11 int mid = (lef+rig) >> 1; 12 construct((index<<1)+1, lef, mid); 13 construct((index<<1)+2, mid+1, rig); 14 segTree[index].counter = 0; 15 }
2、线段树的元素插入

1 /* 插入线段[start,end]到线段树, 同时计数区间次数 */ 2 void insert(int index, int start, int end) 3 { 4 if(segTree[index].left == start && segTree[index].right == end) 5 { 6 ++segTree[index].counter; 7 return; 8 } 9 int mid = (segTree[index].left + segTree[index].right) >> 1; 10 if(end <= mid)//左子树 11 { 12 insert((index<<1)+1, start, end); 13 }else if(start > mid)//右子树 14 { 15 insert((index<<1)+2, start, end); 16 }else//分开来了 17 { 18 insert((index<<1)+1, start, mid); 19 insert((index<<1)+2, mid+1, end); 20 } 21 }
3、线段树的元素查找

1 /* 查询点x的出现次数 2 * 从根节点开始到[x,x]叶子的这条路径中所有点计数相加方为x出现次数 3 */ 4 int query(int index, int x) 5 { 6 if(segTree[index].left == segTree[index].right) // 走到叶子,返回 7 { 8 return segTree[index].counter; 9 } 10 int mid = (segTree[index].left+segTree[index].right) >> 1; 11 if(x <= mid) 12 { 13 return segTree[index].counter + query((index<<1)+1,x); 14 } 15 return segTree[index].counter + query((index<<1)+2,x); 16 }
4、线段树的元素删除

1 void delete_ (int c , int d, int index) 2 { 3 if(c <= segTree[index].left && d >= segTree[index].right) 4 segTree[index].counter--; 5 else 6 { 7 if(c < (segTree[index].left + segTree[index].right)/2 ) delete_( c,d, segTree[index].left); 8 if(d > (segTree[index].left + segTree[index].right)/2 ) delete_( c,d, segTree[index].right); 9 } 10 }
四、线段树的应用
- 区间最值查询问题
- 连续区间修改或者单节点更新的动态查询问题
- 多维空间的动态查询
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· PostgreSQL 和 SQL Server 在统计信息维护中的关键差异
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
· 上周热点回顾(2.17-2.23)