高级数据结构 | 一步一步理解线段树
一、啥是线段树
二、从一个引例理解线段树之美(雾
三、线段树的实战训练
到底啥是线段树
线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。
对于线段树中的每一个非叶子节点[a,b],它的左儿子表示的区间为[a,(a+b)/2],右儿子表示的区间为[(a+b)/2+1,b]。因此线段树是平衡二叉树,最后的子节点数目为N,即整个线段区间的长度。
使用线段树可以快速的查找某一个节点在若干条线段中出现的次数,时间复杂度为O(logN)。
也许你会觉得这个解释对于你有些晦涩难懂,没事,下面我们通过一个例子,进一步体验线段树的魅力(雾
一个简单的引例
问题描述(小黑的不稳定数列)
总所周知,小黑有一串十分神奇的不稳定Cys数列。在这串长度为M的不稳定数列中,其中数列的大小固定,但数列中的数据会发生T次乱码及爆炸,在每一次乱码爆炸发生后,数列中的部分元素值会被随机更新,变得面目全非.
现在,小黑想请你帮个忙:即在每一次数列乱码爆炸后,告诉小黑该税额某个区间内的最小值.
例如对于数列[2, 5, 1, 4, 9, 3],我们可以构造如图1.1的二叉树.特憋需要注意的是,如果你连二叉树都不会,那么请你关闭此帖:
我们可以用线段树来解决这个问题:预处理耗时O(n),查询、更新操作O(logn),需要额外的空间O(n)。根据这个问题我们构造如下的二叉树(背景为白色表示叶子节点,非叶子节点的值是其对应数组区间内的最小值,例如根节点表示数组区间Cys0[0..5]内的最小值是1)
如果你学过树的话,那么你容易发现:线段树近似一棵满二叉树,那么如果要存储线段树,那么需要的空间复杂度是O(n),预处理耗时O(n)。
知道这些后,那么问题来了——对于线段树的操作:创建线段树、查询、节点更新又是如何运作的呢?
About·如何建立线段树
[ccen_cpp]
#include
using namespace std;
const int maxind = 256;
int segTree[maxind * 4 + 10];
int array[maxind];
/* 构造函数,得到线段树 */
void build(int node, int begin, int end)
{
if (begin == end)
segTree[node] = array[begin]; /* 只有一个元素,节点记录该单元素 */
else
{
/* 递归构造左右子树 */
build(2*node, begin, (begin+end)/2);
build(2*node+1, (begin+end)/2+1, end);
/* 回溯时得到当前node节点的线段信息 */
if (segTree[2 * node] <= segTree[2 * node + 1])
segTree[node] = segTree[2 * node];
else
segTree[node] = segTree[2 * node + 1];
}
}
int main()
{
array[0] = 1, array[1] = 2,array[2] = 2, array[3] = 4, array[4] = 1, array[5] = 3;
build(1, 0, 5);
for(int i = 1; i<=20; ++i)
cout<< "seg"<< i << "=" <
About·如何进行区间查询
高级数据结构 | 一步一步理解线段树
一、啥是线段树
二、从一个引例理解线段树之美(雾
三、线段树的实战训练
到底啥是线段树
线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。
对于线段树中的每一个非叶子节点[a,b],它的左儿子表示的区间为[a,(a+b)/2],右儿子表示的区间为[(a+b)/2+1,b]。因此线段树是平衡二叉树,最后的子节点数目为N,即整个线段区间的长度。
使用线段树可以快速的查找某一个节点在若干条线段中出现的次数,时间复杂度为O(logN)。
也许你会觉得这个解释对于你有些晦涩难懂,没事,下面我们通过一个例子,进一步体验线段树的魅力(雾
一个简单的引例
问题描述(小黑的不稳定数列)
总所周知,小黑有一串十分神奇的不稳定Cys数列。在这串长度为M的不稳定数列中,其中数列的大小固定,但数列中的数据会发生T次乱码及爆炸,在每一次乱码爆炸发生后,数列中的部分元素值会被随机更新,变得面目全非.
现在,小黑想请你帮个忙:即在每一次数列乱码爆炸后,告诉小黑该税额某个区间内的最小值.
图1.1 – 小黑的不稳定数列题图
例如对于数列[2, 5, 1, 4, 9, 3],我们可以构造如图1.1的二叉树.特憋需要注意的是,如果你连二叉树都不会,那么请你关闭此帖:
我们可以用线段树来解决这个问题:预处理耗时O(n),查询、更新操作O(logn),需要额外的空间O(n)。根据这个问题我们构造如下的二叉树(背景为白色表示叶子节点,非叶子节点的值是其对应数组区间内的最小值,例如根节点表示数组区间Cys0[0..5]内的最小值是1)
如果你学过树的话,那么你容易发现:线段树近似一棵满二叉树,那么如果要存储线段树,那么需要的空间复杂度是O(n),预处理耗时O(n)。
知道这些后,那么问题来了——对于线段树的操作:创建线段树、查询、节点更新又是如何运作的呢?
About·如何建立线段树
[ccen_cpp]
#include
using namespace std;
const int maxind = 256;
int segTree[maxind * 4 + 10];
int array[maxind];
/* 构造函数,得到线段树 */
void build(int node, int begin, int end)
{
if (begin == end)
segTree[node] = array[begin]; /* 只有一个元素,节点记录该单元素 */
else
{
/* 递归构造左右子树 */
build(2*node, begin, (begin+end)/2);
build(2*node+1, (begin+end)/2+1, end);
/* 回溯时得到当前node节点的线段信息 */
if (segTree[2 * node] <= segTree[2 * node + 1])
segTree[node] = segTree[2 * node];
else
segTree[node] = segTree[2 * node + 1];
}
}
int main()
{
array[0] = 1, array[1] = 2,array[2] = 2, array[3] = 4, array[4] = 1, array[5] = 3;
build(1, 0, 5);
for(int i = 1; i<=20; ++i)
cout<< "seg"<< i << "=" <
About·如何进行区间查询
int Read(){
int x = 0;
char c = getchar();
while(c < '0' || c > '9') c = getchar();
while(c >= '0' && c <= '9') x = x*10 + c-'0' , c = getchar();
return x;
}