这个链接上面对线段树描述得很清楚【线段树】线段树入门之入门
下面谈谈自己的理解:
线段树对多次查询极有优势,因为一般情况下并不需要扫描到树的最底端就能得出结果。
个人感觉线段树也是一种打表(树形的表?)=。=比如哦,求区间的最大值,用线段树的话,方法抽象点的说就是区间打表(个人猜测).....分别对每个区间记录下其最大值,当扫描到这个区间的时候,直接取这个区间存放的最大值,避免再次深入搜索。
线段树更新的话,从根节点向父节点递归,凡是与节点数值变动有影响的节点,均需要更新一下以免出现父节点最大值并不等于所有子节点中最大值这种情况。但是与变动节点没有影响的其余节点,那么则不需要改变。
贴上个人写的代码(注释写得还是很清楚的....):
/*
线段树入门
该例为查询区间的最大值
*/
#include <iostream>
#include <cmath>
using namespace std;
const int MAXNODE = 2000006;
const int MAX = 1000003;
struct Node
{
int value;
int left, right;
};
Node node[MAXNODE];
int root[MAX]; //对线段树的根节点进行打表,记录下每个根节点所对应的树的编号
void BuildTree(int i, int left, int right) //建立一棵树,i代表起始点,l,r代表线段树长度
{
node[i].left = left;
node[i].right = right;
node[i].value = 0;
if (left == right)
{
root[left] = i; //记录叶子节点所对应的树的序号
return;
}
BuildTree(i << 1, left, (int)((left + right) / 2));
BuildTree((i << 1) + 1, (int)((left + right) / 2) + 1, right);
}
void UpdateTree(int i) //从下到上更新节点,在函数外部更改根节点,i代表变动节点
{
if (i == 1)
return;
int x = i / 2; //树节点标号向上更改
int a = node[x << 1].value; //记录左子节点值
int b = node[(x << 1) + 1].value; //记录右子节点值
node[x].value = (a > b)? a : b;
UpdateTree(x);
}
int Max = -1;
void Query(int i, int l, int r) //查询节点,i代表查询起始节点编号,l,r代表要查询的线段区间
{
if (node[i].left == l && node[i].right == r) //找到匹配区间段,记录下最大值并退出
{
Max = (Max < node[i].value)? node[i].value : Max;
return;
}
//如果不满足整体匹配,那么对查询段进行拆分
i = i << 1; //首先进入左子树查询
if (l <= node[i].right) //如果满足查询段中存在从起点数一段属于左子树
{
if (r <= node[i].right) //如果查询段全部属于左子树
Query(i, l, r); //递归进入左子树再次查询
else
Query(i, l, node[i].right); //否则部分进入左子树查询
}
i += 1; //接下来查询右子树
if (r >= node[i].left)
{
if (l >= node[i].left) //如果全部属于右子树那么递归进入右子树继续查询
Query(i, l, r);
else
Query(i, node[i].left, r); //否则属于右子树的线段递归右子树查询
}
}
int main()
{
BuildTree(1, 1, 5);
node[root[1]].value = 6;
node[root[5]].value = 4;
UpdateTree(root[1]);
UpdateTree(root[5]);
Query(1, 1, 5);
cout << Max;
return 0;
}