线段树(区间树)之区间染色和4n推导过程
前言
线段树(区间树)是什么呢?有了二叉树、二分搜索树,线段树又是干什么的呢?最经典的线段树问题:区间染色;正如它的名字而言,主要解决区间的问题
一、线段树说明
1、什么是线段树?
线段树首先是二叉树,并且是平衡二叉树(它是一 棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树),并且具有二分性质。
如下图,就是一颗线段树:

假如,用数组表示线段树,如果区间有n个元素,数组表示需要有多少节点?
2、4n节点推导过程
要进行一下
,如果对推导过程不感兴趣的,可以直接记住结论,需要4n个节点,推导过程如下图: PS:依旧是全博客园最丑图,当感觉有进步啊!是不是推荐一下,鼓励一下啊

说明:感觉用尽了洪荒之力,才推导出来了。感觉高考之后再也不会用到等比公式了,但又用到了,还是缘分未尽啊,哈哈哈!最后,都放弃了,一直推导不出来,忘却了最后一层的null,假设是满二叉树,按最大值进行估算,所以4n是完全够大的!
二、为什么要使用线段树
线段树主要解决一些区间问题的,如下:
1、区间染色
有一面墙,长度为n,每次选择一段墙进行染色,m次操作之后,我们可以看见多少种颜色?
2、区间查询
查询区间[i,j]的最大值、最小值,或者区间数字和;实质:基于区间的统计查询。
例如:2018年注册用户中消费最高的用户?消费最低的用户?学习最长时间的用户?
三、代码实现
1、创建线段树
二叉树具有天然递归性质,所以用递归相对简单,用迭代也是可以的,我才用递归实现,代码如下:
template<class T>
class SegmentTree {
private:
T *tree;
T *data;
int size;
std::function<T(T, T)> function; int leftChild(int index) { //左孩子下标;例如用数组存储,根节点是下标0,则左孩子为1,右孩子为2
return index * + ;
} int rightChild(int index) { //右孩子下标
return index * + ;
} void buildSegmentTree(int treeIndex, int l, int r) {
if (l == r) {
tree[treeIndex] = data[l];
return;
}
int leftTreeIndex = leftChild(treeIndex);
int rightTreeIndex = rightChild(treeIndex);
int mid = l + (r - l) / ; //中间值求法,防止整型溢出 buildSegmentTree(leftTreeIndex, l, mid); //构建左子树
buildSegmentTree(rightTreeIndex, mid + , r); //构建右子树
tree[treeIndex] = function(tree[leftTreeIndex], tree[rightTreeIndex]);
}
public:
SegmentTree(T arr[], int n, std::function<T(T, T)> function) { //构造函数,构建一棵树
this->function = function;
data = new T[n];
for (int i = ; i < n; ++i) {
data[i] = arr[i];
}
tree = new T[n * ]; //分配4n节点
size = n;
buildSegmentTree(, , size - );
}
};
2、线段树查询
线段树具有二分查找性质,所以二分查找那种思路就可以了,代码如下:
T query(int treeIndex, int l, int r, int queryL, int queryR) {
if (l == queryL && r == queryR) {
return tree[treeIndex];
}
int mid = l + (r - l) / ;
int leftTreeIndex = leftChild(treeIndex);
int rightTreeIndex = rightChild(treeIndex);
if (queryL >= mid + ) {
return query(rightTreeIndex, mid + , r, queryL, queryR);
} else if (queryR <= mid) {
return query(leftTreeIndex, l, mid, queryL, queryR);
}
T leftResult = query(leftTreeIndex, l, mid, queryL, mid);
T rightResult = query(rightTreeIndex, mid + , r, mid + , queryR);
return function(leftResult, rightResult);
}
T query(int queryL, int queryR) {
assert(queryL >= && queryL < size && queryR >= && queryR < size && queryL <= queryR);
return query(, , size - , queryL, queryR);
}
3、整体代码
SegmentTree.h如下:
#ifndef SEGMENT_TREE_SEGMENTTREE_H
#define SEGMENT_TREE_SEGMENTTREE_H #include <cassert>
#include <functional> template<class T>
class SegmentTree {
private:
T *tree;
T *data;
int size;
std::function<T(T, T)> function; int leftChild(int index) {
return index * + ;
} int rightChild(int index) {
return index * + ;
} void buildSegmentTree(int treeIndex, int l, int r) {
if (l == r) {
tree[treeIndex] = data[l];
return;
}
int leftTreeIndex = leftChild(treeIndex);
int rightTreeIndex = rightChild(treeIndex);
int mid = l + (r - l) / ; buildSegmentTree(leftTreeIndex, l, mid);
buildSegmentTree(rightTreeIndex, mid + , r);
tree[treeIndex] = function(tree[leftTreeIndex], tree[rightTreeIndex]);
} T query(int treeIndex, int l, int r, int queryL, int queryR) {
if (l == queryL && r == queryR) {
return tree[treeIndex];
} int mid = l + (r - l) / ;
int leftTreeIndex = leftChild(treeIndex);
int rightTreeIndex = rightChild(treeIndex); if (queryL >= mid + ) {
return query(rightTreeIndex, mid + , r, queryL, queryR);
} else if (queryR <= mid) {
return query(leftTreeIndex, l, mid, queryL, queryR);
} T leftResult = query(leftTreeIndex, l, mid, queryL, mid);
T rightResult = query(rightTreeIndex, mid + , r, mid + , queryR);
return function(leftResult, rightResult);
} public:
SegmentTree(T arr[], int n, std::function<T(T, T)> function) {
this->function = function;
data = new T[n];
for (int i = ; i < n; ++i) {
data[i] = arr[i];
}
tree = new T[n * ];
size = n;
buildSegmentTree(, , size - );
} int getSize() {
return size;
} T get(int index) {
assert(index >= && index < size);
return data[index];
} T query(int queryL, int queryR) {
assert(queryL >= && queryL < size && queryR >= && queryR < size && queryL <= queryR);
return query(, , size - , queryL, queryR);
} void print() {
std::cout << "[";
for (int i = ; i < size * ; ++i) {
if (tree[i] != NULL) {
std::cout << tree[i];
} else {
std::cout << "";
}
if (i != size * - ) {
std::cout << ", ";
}
}
std::cout << "]" << std::endl;
}
}; #endif //SEGMENT_TREE_SEGMENTTREE_H
main.cpp如下:
#include <iostream>
#include "SegmentTree.h" int main() {
int nums[] = {-, , , -, , -};
SegmentTree<int> *segmentTree = new SegmentTree<int>(nums, sizeof(nums) / sizeof(int), [](int a, int b) -> int {
return a + b;
});
std::cout << segmentTree->query(,) << std::endl;
segmentTree->print();
return ;
}
4、演示
运行结果,如下:

5、时间复杂度分析
更新 O(logn)
查询 O(logn)
线段树(区间树)之区间染色和4n推导过程的更多相关文章
- poj-----(2528)Mayor's posters(线段树区间更新及区间统计+离散化)
Mayor's posters Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 43507 Accepted: 12693 ...
- POJ 3468 A Simple Problem with Integers(线段树 成段增减+区间求和)
A Simple Problem with Integers [题目链接]A Simple Problem with Integers [题目类型]线段树 成段增减+区间求和 &题解: 线段树 ...
- BZOJ 3110 ZJOI 2013 K大数查询 树套树(权值线段树套区间线段树)
题目大意:有一些位置.这些位置上能够放若干个数字. 如今有两种操作. 1.在区间l到r上加入一个数字x 2.求出l到r上的第k大的数字是什么 思路:这样的题一看就是树套树,关键是怎么套,怎么写.(话说 ...
- poj 2892---Tunnel Warfare(线段树单点更新、区间合并)
题目链接 Description During the War of Resistance Against Japan, tunnel warfare was carried out extensiv ...
- hdu 3974 线段树 将树弄到区间上
Assign the task Time Limit: 15000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) ...
- POJ 3468 A Simple Problem with Integers(线段树模板之区间增减更新 区间求和查询)
A Simple Problem with Integers Time Limit: 5000MS Memory Limit: 131072K Total Submissions: 140120 ...
- 约会安排---hdu4553(线段树,麻烦的区间覆盖)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4553 算是poj3667的加强版,建立两颗线段树,一个是DS区间,另一个是NS区间.那么根据题意, ...
- 【BZOJ】1798: [Ahoi2009]Seq 维护序列seq 线段树多标记(区间加+区间乘)
[题意]给定序列,支持区间加和区间乘,查询区间和取模.n<=10^5. [算法]线段树 [题解]线段树多重标记要考虑标记与标记之间的相互影响. 对于sum*b+a,+c直接加上即可. *c后就是 ...
- HDU 1698 【线段树,区间修改 + 维护区间和】
题目链接 HDU 1698 Problem Description: In the game of DotA, Pudge’s meat hook is actually the most horri ...
随机推荐
- Linux网络文件系统的实现与调试
NFS协议 NFS (网络文件系统)不是传统意义上的文件系统,而是访问远程文件系统的网络协议.整个NFS服务的TCP/IP协议栈如下图所示,NFS是应用层协议,表示层是XDR,会话层是RPC,传输层同 ...
- NOIP2013提高组 T2 火柴排队
一开始看也想不到这居然要用到逆序对,归并排序. 先来看看题目: 涵涵有两盒火柴,每盒装有 n 根火柴,每根火柴都有一个高度. 现在将每盒中的火柴各自排成一列, 同一列火柴的高度互不相同, 两列火柴之间 ...
- typeof获取变量的数据类型 javascript
获取变量的数据类型:typeof <!DOCTYPE html> <html lang="en"> <head> <meta charse ...
- Tensor索引操作
#Tensor索引操作 ''''' Tensor支持与numpy.ndarray类似的索引操作,语法上也类似 如无特殊说明,索引出来的结果与原tensor共享内存,即修改一个,另一个会跟着修改 ''' ...
- Autograd:自动微分
Autograd 1.深度学习的算法本质上是通过反向传播求导数,Pytorch的Autograd模块实现了此功能:在Tensor上的所有操作,Autograd都能为他们自动提供微分,避免手动计算导数的 ...
- vue的风格指南(必要的)
1.v-if与v-for不要放在同一个元素上 当 v-if 与 v-for 一起使用时,v-for 具有比 v-if 更高的优先级.永远不要把 v-if 和 v-for 同时用在同一个元素上. 一般我 ...
- 2018-2019-20175324实验一《Java开发环境的熟悉》实验报告
2018-2019-20175324实验一<Java开发环境的熟悉>实验报告 实验内容与结果 一.Java开发环境的熟悉-1 1.实验要求: 0 参考实验要求 1 建立“自己学号exp ...
- 《SQL优化入门》讲座总结
MySQL运行机制 MySQL每个query只能运行在一个CPU上,更多的CPU,更快的CPU会更有利于并发 MySQL执行计划 Using filesort: 表示无法利用索引完成排序,也有可能是因 ...
- [LeetCode] Minimum Cost to Merge Stones 混合石子的最小花费
There are N piles of stones arranged in a row. The i-th pile has stones[i] stones. A move consists ...
- swust oj 1010
折半查找的实现 1000(ms) 10000(kb) 2877 / 11213 编写程序实现折半查找算法. 输入 第一行是查找表的长度n 第二行是查找表中的数据元素 : 第三行是要查找的数据元素的关键 ...