线段树(nb)
今天刚学习了线段树,赶紧趁热打了两遍模版
下面都是线段树的基本操作,这个板子是维护的区间中的最大值,当然修改change和build包括线段树中的data可以维护区间上的不同信息。
首先介绍一下线段树这种数据结构吧
线段树是一种基于分治思想的二叉树结构,用于在区间上进行信息的统计与维护
板子的一些默认信息
1、线段树的每个结点都代表一个区间。
2、线段树具有唯一的跟结点,代表的区间是整个统计范围,即[1~N]。
3、线段树的每个叶子结点都代表一个长度为1的元区间[x, x]。
4、 对于每个内部节点[l, r],它的左子结点是[l, mid], 右子结点是[mid + 1, r]
#include <iostream>
using namespace std;
//线段树是基于分治的思想实现的
//struct数组存储线段树
struct SegmentTree
{
int l, r;
int data;
}t[100 * 4];
int a[100];
//用线段树维护每个区间的最大值
void build(int p, int l, int r)
{
t[p].l = l, t[p].r = r;
if(r == l) {t[p].data = a[l]; return ;}
int mid = (l + r) >> 1;
build(p*2, l, mid);
build(p*2 + 1, mid + 1, r);
t[p].data = max(t[p * 2].data, t[p*2 + 1].data);
}
//调用入口
// build(1, 1, n);
//线段树的单点修改
void change(int p, int x, int v)
{
if(t[p].l == t[p].r) {t[p].data = v; return ;}//找到要修改的叶子结点
int mid = (t[p].l + t[p].r) >> 1;
if(x <= mid) change(p * 2, x ,v); //x属于左半区间
else change(p * 2 + 1, x, v); // 行属于右半区间
t[p].data = max(t[p * 2].data, t[p * 2 + 1].data);//从下往上更新信息
}
//线段树的区间查询
int ask(int p, int l, int r)
{
if(l <= t[p].l && r >= t[p].r) return t[p].data;
int mid = (t[p]. l + t[p].r) >> 1;
int val = -(1 << 30); //初始化为负无穷大
if(l <= mid) val = max(val, ask(p * 2, l, r));
if(r > mid) val = max(val, ask(p * 2 + 1, l ,r));
return val;
}
//调用出口 cout << ask(1, 1, r) << endl;
int main()
{
int n;cin >> n;
for(int i = 1; i <= n; ++ i) cin >> a[i];
build(1, 1, n);
cout << ask(1, 2, 3);
}
若要维护区间最大连续子段和
#include <iostream>
using namespace std;
//线段树是基于分治的思想实现的
//struct数组存储线段树
struct SegmentTree
{
int l, r;
int lmax, rmax, sum, data;
}t[100 * 4];
int a[100];
//用线段树维护每个区间的信息
void build(int p, int l, int r)
{
t[p].l = l, t[p].r = r;
if(r == l)
{
t[p].sum = a[l];
t[p].lmax = a[l];
t[p].rmax = a[l];
t[p].data = a[l];
return ;
}
int mid = (l + r) >> 1;
build(p*2, l, mid);
build(p*2 + 1, mid + 1, r);
t[p].lmax = max(t[p * 2].lmax, t[p * 2].sum + t[p * 2 + 1].lmax);
t[p].rmax = max(t[p * 2 + 1].rmax, t[p * 2 + 1].sum + t[p * 2].rmax);
t[p].data = max(t[p * 2].data, t[p * 2 + 1].data);
t[p].data = max(t[p].data, t[p * 2].rmax + t[p * 2 + 1].lmax);
}
//调用入口
// build(1, 1, n);
//线段树的单点修改
void change(int p, int x, int v)
{
if(t[p].l == t[p].r) {t[p].data = v; return ;}//找到要修改的叶子结点
int mid = (t[p].l + t[p].r) >> 1;
if(x <= mid) change(p * 2, x ,v); //x属于左半区间
else change(p * 2 + 1, x, v); // 行属于右半区间
//从下往上更新信息
t[p].sum = t[p * 2].sum + t[p * 2 + 1].sum;
t[p].lmax = max(t[p * 2].lmax, t[p * 2].sum + t[p * 2 + 1].lmax);
t[p].rmax = max(t[p * 2 + 1].rmax, t[p * 2 + 1].sum + t[p * 2].rmax);
t[p].data = max(t[p * 2].data, t[p * 2 + 1].data);
t[p].data = max(t[p].data, t[p * 2].rmax + t[p * 2 + 1].lmax);
}
//线段树的区间查询
int ask(int p, int l, int r)
{
if(l <= t[p].l && r >= t[p].r) return t[p].data;
int mid = (t[p]. l + t[p].r) >> 1;
int val = -(1 << 30); //初始化为负无穷大
if(l <= mid) val = max(val, ask(p * 2, l, r));
if(r > mid) val = max(val, ask(p * 2 + 1, l ,r));
return val;
}
//调用出口 cout << ask(1, 1, r) << endl;
int main()
{
int n;cin >> n;
for(int i = 1; i <= n; ++ i) cin >> a[i];
build(1, 1, n);
cout << ask(1, 1, 5);
}
可以看出我们只需要修改build和change部分即可,只需要加入我们想要维护区间的额外信息。
线段树也太好用了吧QWQ!!!