首页 > 代码库 > 线段树(一)

线段树(一)

问题:

先抛出一个问题,坐标轴上有若干线段,现在给定若干个点,对于每个点,求出包含点的线段的数量

如果用常规的解法,时间复杂度是O(mn),空间复杂度是O(m + n)

能不能降低一下时间复杂度呢?答案是肯定的,这些线段里有大量相交或者覆盖的线段,而上面的解法显然没有利用这些信息,导致时间复杂度较高,现在我们引入

一个数据结构,线段树(Segment Tree),从Wikipedia抄过来定义如下:

In computer science, a segment tree is a tree data structure for storing intervals, or segments. It allows querying which of the 
stored segments contain a given point. It can be implemented as a dynamic structure. A similar data structure is the interval tree.

第一次看我也是一头雾水,什么也没说嘛,下面用一张图解释一下:

A. 建树

 

给出8条线段,S[1-8],区间分别为[1,3],[2,4],[3,5],[4,6],[7,8],[7,9],[9,10],[8,10],总区间为[1,10],一棵线段树要覆盖区间,因此用区间[1,10]

建立一棵线段树,如上图,可以看出,线段树是一棵二叉平衡树,简单性质如下:

    1. 对于非叶子节点:r[a,b](a < b) r的左儿子节点的区间为[a,(a + b) / 2];r的右儿子节点的区间为[(a + b) / 2 + 1, b]

    2. 叶子节点对应一个点,也即r[a,b](a = b)

 

A. 插入线段

那么建立起这样一棵树后,怎么插入一条线段呢?方法很简单,以插入S8为例,看图:

可以看出,方法很简单:

1. 从根节点r(a,b)开始, 待插线段s(c,d)

    1.0 如果a==b,转到2

    1.1 如果d <= (a + b) / 2,以r的左孩子为根,转到1

    1.2 如果c > (a + b) / 2,以r的右孩子为根,转到1

    1.3 否则搜索r的左孩子和右孩子

2. 节点的值+1

C. 查询

最后查询某个点的包含次数,就非常简单了,沿着根节点搜索到叶子节点,对应的叶子节点的值即为包含次数

 

给出代码:

 1 /*建树*/ 2 void build_ST(int i, int left, int right) { 3     st[i].left = left; 4     st[i].right = right; 5     if(left == right) { 6         st[i].count = 0; 7         return;     8     }    9     build_ST(i * 2, left, (left + right) / 2); 10     build_ST(i * 2 + 1, (left + right) / 2 + 1, right);11 }
 1 /*插入线段*/ 2 void insert_ST(int i, int left, int right) { 3     int l = st[i].left; 4     int r = st[i].right; 5     if(l == r) { 6         st[i].count++ 7     } 8     else if(right <= (r + l) / 2) { 9         insert_ST(i * 2, left, right);10     }11     else if(left > (r + l) / 2) {12         insert_ST(i * 2 + 1, left, right);13     }14     else {15         insert_ST(i * 2, left, right);16         insert_ST(i * 2 + 1, left, right);17     }18 }
 1 int query_ST(int i, int value) { 2     int l = st[i].left; 3     int r = st[i].right; 4     if(l == r)  5         return st[i].count; 6     else if(value <= (r + l) / 2) 7         return query_ST(i * 2, value); 8     else 9         return query_ST(i * 2 + 1, value);10 }

 

最后看一下复杂度:

操作时间复杂度空间复杂度
建树O(points)O(points)
插入

O(points)

 
查询O(logpoints) 

 

 

 

 

 

 

由于本人水平有限,文中难免有不当和错误指出,欢迎大家批评指正,愿共同进步!!!

 

线段树(一)