您的位置:首页 > 理论基础 > 数据结构算法

数据结构之线段树

2013-08-22 13:15 183 查看
线段树也叫区间树,顾名思义,线段树是一种基于区间的树,每个节点表示一个“线段”或“区间”。树的根节点表示是“整体”的区间,左右子树分别表示这个区间的左半边和右半边。

function 以节点v为根建树、v对应区间为[l,r] {

对节点v初始化

if (l!=r) {

以v的左孩子为根建树、区间为[l,(l+r)/2]

以v的右孩子为根建树、区间为[(l+r)/2+1,r]

}

}

线段树的关键在于如何定义树节点,以及如果构建(插入)树节点。

1.树节点的定义

POJ 3264 Balanced Lineup

给定Q (1 ≤ Q ≤ 200,000)个数A1,A2 … AQ,,多次求任一区间Ai – Aj中最大数和最小数的差。

本题树节点结构:

struct CNode

{

int L,R; //区间起点和终点

int nMin,nMax;//本区间里的最大最小值

CNode * pLeft, * pRight;

};

POJ 3468 A Simple Problem with Integers

给定Q (1 ≤ Q ≤ 100,000)个数A1,A2 … AQ,,以及可能多次进行的两个操作:

1) 对某个区间Ai … Aj的个数都加n(n可变)

2) 求某个区间Ai … Aj的数的和

本题树节点结构:

struct CNode

{

int L ,R; //区间起点和终点

CNode * pLeft, * pRight;

long long nSum; //原来的和

long long Inc; //增量c的累加

}; //本节点区间的和实际上是nSum+Inc*(R-L+1)

POJ 2528 Mayor's posters

给定一些海报,可能互相重叠,告诉你每个海报宽度(高度都一样)和先后叠放次序,问没有被完全盖住的海报有多少张。

struct CNode

{

int L,R;

bool bCovered;

CNode * pLeft, * pRight;

};

bCovered表示本区间是否已经完全被海报盖住

关键: 插入数据的顺序 ------ 从底至上依次插入每张海报

2.插入过程(构建)

以下是关于区间覆盖问题的程序描述:

#include<iostream>

using namespace std;

/* 线段树求线段覆盖长度 */

#define BORDER 100 // 设线段端点坐标不超过100

struct Node // 线段树

{

int left;

int right;

int isCover; // 标记是否被覆盖

}segTree[4*BORDER];

/* 构建线段树 根节点开始构建区间[lef,rig]的线段树*/

void construct(int index, int lef, int rig)

{

segTree[index].left = lef;

segTree[index].right = rig;

if(rig - 1 == lef) // 到单位1线段

{

segTree[index].isCover = 0;

return;

}

int mid = (lef+rig) >> 1;

construct((index<<1)+1, lef, mid);

construct((index<<1)+2, mid, rig); // 非mid+1,线段覆盖[mid,mid+1]

segTree[index].isCover = 0;

}

/* 插入线段[start,end]到线段树, 同时标记覆盖 */

void insert(int index, int start, int end)

{

if(segTree[index].isCover == 1) return; // 如已覆盖,没必要继续向下插

if(segTree[index].left == start && segTree[index].right == end)

{

segTree[index].isCover = 1;

return;

}

int mid = (segTree[index].left + segTree[index].right) >> 1;

if(end <= mid)

{

insert((index<<1)+1, start, end);

}else if(start >= mid) // 勿漏=

{

insert((index<<1)+2, start, end);

}else

{

insert((index<<1)+1, start, mid);

insert((index<<1)+2, mid, end);

// 注:不是mid+1,线段覆盖,不能漏[mid,mid+1]

}

}

/* 计算线段覆盖长度 */

int Count(int index)

{

if(segTree[index].isCover == 1)

{

return segTree[index].right - segTree[index].left;

}else if(segTree[index].right - segTree[index].left == 1)

{

return 0;

}

return Count((index<<1)+1) + Count((index<<1)+2);

}

/* 测试线段 answer: 71 */

int segment[10][2] = {

5, 8, 10, 45, 0, 7,

2, 3, 3, 9, 13, 26,

15, 38, 50, 67, 39, 42,

70, 80

};

void main()

{

construct(0,0,100); // 构建[0,100]线段树

for(int i = 0; i < 10; ++i) // 插入测试线段

{

insert(0,segment[i][0],segment[i][1]);

}

printf("the cover length is %d\n", Count(0));

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: