java 区间树_线段树(区间树)之区间染色和4n推导过程
前言
線段樹(區間樹)是什么呢?有了二叉樹、二分搜索樹,線段樹又是干什么的呢?最經典的線段樹問題:區間染色;正如它的名字而言,主要解決區間的問題
一、線段樹說明
1、什么是線段樹?
線段樹首先是二叉樹,并且是平衡二叉樹(它是一 棵空樹或它的左右兩個子樹的高度差的絕對值不超過1,并且左右兩個子樹都是一棵平衡二叉樹),并且具有二分性質。
如下圖,就是一顆線段樹:
假如,用數組表示線段樹,如果區間有n個元素,數組表示需要有多少節點?
2、4n節點推導過程
要進行一下
,如果對推導過程不感興趣的,可以直接記住結論,需要4n個節點,推導過程如下圖: PS:依舊是全博客園最丑圖,當感覺有進步啊!是不是推薦一下,鼓勵一下啊
說明:感覺用盡了洪荒之力,才推導出來了。感覺高考之后再也不會用到等比公式了,但又用到了,還是緣分未盡啊,哈哈哈!最后,都放棄了,一直推導不出來,忘卻了最后一層的null,假設是滿二叉樹,按最大值進行估算,所以4n是完全夠大的!
二、為什么要使用線段樹
線段樹主要解決一些區間問題的,如下:
1、區間染色
有一面墻,長度為n,每次選擇一段墻進行染色,m次操作之后,我們可以看見多少種顏色?
2、區間查詢
查詢區間[i,j]的最大值、最小值,或者區間數字和;實質:基于區間的統計查詢。
例如:2018年注冊用戶中消費最高的用戶?消費最低的用戶?學習最長時間的用戶?
三、代碼實現
1、創建線段樹
二叉樹具有天然遞歸性質,所以用遞歸相對簡單,用迭代也是可以的,我才用遞歸實現,代碼如下:
template
classSegmentTree {private:
T*tree;
T*data;intsize;
std::functionfunction;int leftChild(intindex) { //左孩子下標;例如用數組存儲,根節點是下標0,則左孩子為1,右孩子為2return index * 2 + 1;
}int rightChild(intindex) { //右孩子下標return index * 2 + 2;
}void buildSegmentTree(int treeIndex, int l, intr) {if (l ==r) {
tree[treeIndex]=data[l];return;
}int leftTreeIndex =leftChild(treeIndex);int rightTreeIndex =rightChild(treeIndex);int mid = l + (r - l) / 2; //中間值求法,防止整型溢出
buildSegmentTree(leftTreeIndex, l, mid); //構建左子樹
buildSegmentTree(rightTreeIndex, mid+ 1, r); //構建右子樹
tree[treeIndex]=function(tree[leftTreeIndex], tree[rightTreeIndex]);
}public:
SegmentTree(T arr[],int n, std::functionfunction) { //構造函數,構建一棵樹this->function =function;
data= newT[n];for (int i = 0; i < n; ++i) {
data[i]=arr[i];
}
tree= new T[n * 4]; //分配4n節點
size=n;
buildSegmentTree(0, 0, size - 1);
}
};
2、線段樹查詢
線段樹具有二分查找性質,所以二分查找那種思路就可以了,代碼如下:
T query(int treeIndex, int l, int r, int queryL, intqueryR) {if (l == queryL && r ==queryR) {returntree[treeIndex];
}int mid = l + (r - l) / 2;int leftTreeIndex =leftChild(treeIndex);int rightTreeIndex =rightChild(treeIndex);if (queryL >= mid + 1) {return query(rightTreeIndex, mid + 1, r, queryL, queryR);
}else if (queryR <=mid) {returnquery(leftTreeIndex, l, mid, queryL, queryR);
}
T leftResult=query(leftTreeIndex, l, mid, queryL, mid);
T rightResult= query(rightTreeIndex, mid + 1, r, mid + 1, queryR);returnfunction(leftResult, rightResult);
}
T query(int queryL, intqueryR) {
assert(queryL>= 0 && queryL < size && queryR >= 0 && queryR < size && queryL <=queryR);return query(0, 0, size - 1, queryL, queryR);
}
3、整體代碼
SegmentTree.h如下:
#ifndef SEGMENT_TREE_SEGMENTTREE_H#define SEGMENT_TREE_SEGMENTTREE_H#include#includetemplate
classSegmentTree {private:
T*tree;
T*data;intsize;
std::functionfunction;int leftChild(intindex) {return index * 2 + 1;
}int rightChild(intindex) {return index * 2 + 2;
}void buildSegmentTree(int treeIndex, int l, intr) {if (l ==r) {
tree[treeIndex]=data[l];return;
}int leftTreeIndex =leftChild(treeIndex);int rightTreeIndex =rightChild(treeIndex);int mid = l + (r - l) / 2;
buildSegmentTree(leftTreeIndex, l, mid);
buildSegmentTree(rightTreeIndex, mid+ 1, r);
tree[treeIndex]=function(tree[leftTreeIndex], tree[rightTreeIndex]);
}
T query(int treeIndex, int l, int r, int queryL, intqueryR) {if (l == queryL && r ==queryR) {returntree[treeIndex];
}int mid = l + (r - l) / 2;int leftTreeIndex =leftChild(treeIndex);int rightTreeIndex =rightChild(treeIndex);if (queryL >= mid + 1) {return query(rightTreeIndex, mid + 1, r, queryL, queryR);
}else if (queryR <=mid) {returnquery(leftTreeIndex, l, mid, queryL, queryR);
}
T leftResult=query(leftTreeIndex, l, mid, queryL, mid);
T rightResult= query(rightTreeIndex, mid + 1, r, mid + 1, queryR);returnfunction(leftResult, rightResult);
}public:
SegmentTree(T arr[],int n, std::functionfunction) {this->function =function;
data= newT[n];for (int i = 0; i < n; ++i) {
data[i]=arr[i];
}
tree= new T[n * 4];
size=n;
buildSegmentTree(0, 0, size - 1);
}intgetSize() {returnsize;
}
Tget(intindex) {
assert(index>= 0 && index
}
T query(int queryL, intqueryR) {
assert(queryL>= 0 && queryL < size && queryR >= 0 && queryR < size && queryL <=queryR);return query(0, 0, size - 1, queryL, queryR);
}voidprint() {
std::cout<< "[";for (int i = 0; i < size * 4; ++i) {if (tree[i] !=NULL) {
std::cout<
}else{
std::cout<< "0";
}if (i != size * 4 - 1) {
std::cout<< ",";
}
}
std::cout<< "]" <<:endl>
}
};#endif //SEGMENT_TREE_SEGMENTTREE_H
View Code
main.cpp如下:
#include #include"SegmentTree.h"
intmain() {int nums[] = {-2, 0, 3, -5, 2, -1};
SegmentTree *segmentTree = new SegmentTree(nums, sizeof(nums) / sizeof(int), [](int a, int b) -> int{return a +b;
});
std::cout<< segmentTree->query(2,5) <<:endl>
segmentTree->print();return 0;
}
4、演示
運行結果,如下:
5、時間復雜度分析
更新 O(logn)
查詢 O(logn)
總結
以上是生活随笔為你收集整理的java 区间树_线段树(区间树)之区间染色和4n推导过程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: c++调用mysql存储过程_C++中A
- 下一篇: oracle avg分析函数,分析函数之