【数据结构】线段树

本文详细介绍了线段树的概念、特点及应用。通过实例演示如何利用线段树进行区间查询和更新操作,提供了一种高效解决区间问题的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

    啦啦啦啦啦啦线段树是个好东西- -
    好吧并没有什么好的- -但貌似还是很好啊- -
    线段树就是一棵树!
    顾名思义(又是这个词),就是求关于一段的某些什么什么东西。比如区间最大值啊什么的。

引用百科知识:

线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。

对于线段树中的每一个非叶子节点[a,b],它的左儿子表示的区间为[a,(a+b)/2],右儿子表示的区间为[(a+b)/2+1,b]。因此线段树是平衡二叉树,最后的子节点数目为N,即整个线段区间的长度。

使用线段树可以快速的查找某一个节点在若干条线段中出现的次数,时间复杂度为O(logN)。而未优化的空间复杂度为2N,因此有时需要离散化让空间压缩。

线段树至少支持下列操作:

Insert(t,x):将包含在区间 int 的元素 x 插入到树t中;

Delete(t,x):从线段树 t 中删除元素 x;

Search(t,x):返回一个指向树 t 中元素 x 的指针。

恩就是这样,我来画个图说明存储的数据吧:
【数据结构】线段树 - 李kuandui - 神殇KD灬度阡陌
    差不多是这个样子的。

    明显的,假设我们建立一棵线段树,它的时间复杂度的o(n),修改一个或是查询一个都是o(nlogn)
    为嘛?不为嘛!

    怎么求呢?拿修改来说,主要是运用递归的思想,一路往下找,找到要求的位置,修改值,再一路向上修改最大值什么的。

    而查找一个最大值呢?就是在找包含关系。

    如有8个数,求第2到第7的最大值:
    在1~8找2~7——>在1~4找2~7——>在1~2找2~7——>在2找2~7(发现2包含在2~7内,返回2的值)——>在3~4找2~7(发现3~4包含在2~7内,返回3~4的值)——>在5~8找2~7——>在5~6找2~7(······)——>在7~8找2~7——>在7找2~7

    就是这样(好累)。
    在找的过程中发现,因为区间是连续的,所以一段要么是完全被包含,要么是分叉继续找,而若是分叉部分完全被包含,则被包含的区域是连在一起的,不存在像【取了1~2、5~6而其他不去】的情况。
【数据结构】线段树 - 李kuandui - 神殇KD灬度阡陌
    如上图,黑色部分表示被包含。而我们可以看出,相对于左子树,其被包含部分都在右边;而相对于右子树,其被包含部分都在左边。

    讲了这么多,程序就直接放上去吧(CXB)

#include <iostream> using namespace std; const int maxN =1000007; const int oo = 0x0fffffff; struct Tnode { int l, r; int max; int lc, rc; }; Tnode f[maxN]; int fp=0; int root; int getPoint(int l, int r, int max) { fp++; f[fp].l = l; f[fp].r = r; f[fp].max = max; return fp; }//存数据 int create(int l, int r) { int now = getPoint(l,r,-oo); if (l<r) { f[now].lc =create(l, (l+r)/2); f[now].rc= create((l+r)/2+1, r); } return now; }//建立一棵线段树 void update(int root , int p, int x) { int l = f[root].l; int r = f[root].r; if ( p<l || r<p ) return ; if (l==r) { f[root].max=x; return; }//找到要修改的点,进行修改 update(f[root].lc, p, x);//往左边找要修改的值 update(f[root].rc, p, x);//往右边找要修改的值 f[root].max = max ( f [ f[root].lc ].max, f [ f[root].rc ].max );//修改当前节点最大值 }//修改第p个数据 int query(int root,int l,int r) { if (l==f[root].l && r==f[root].r) return f[root].max; int mid = (f[root].l+f[root].r) / 2; int ans = -oo; if (l <= mid) ans = max(ans,query(f[root].lc,l,min(mid,r)));//查找左边最大值 if (r >= mid+1) ans = max(ans,query(f[root].rc,max(mid+1,l),r));//查找右边最大值 return ans; }//查询一段的最大值 int n; int q; int main() { cin >> n; root = create(1,n); cin >> q; char oper; int l,r,p,v; for (int i=0;i<q;++i){ cin >> oper; if (oper == 'F')//找一段的最大值 { cin >> l >> r; cout << query(root,l,r) << endl; } else if (oper == 'I')//修改一个值 { cin >> p >> v; update(root,p,v); } } return 0; }

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值