线段树初识

来源:互联网 发布:hdfs如何查看数据字段 编辑:程序博客网 时间:2024/06/11 15:27

参考博文:http://www.cnblogs.com/TenosDoIt/p/3453089.html

参考视频:https://www.youtube.com/watch?v=ZBHKZF5w4YU&list=PLrmLmBdmIlpv_jNDXtJGYTPNQ2L1gdHxu&index=22

最近在看数据结构的相关知识,想着看点东西总该写一写总结一下心理才踏实;


线段树是什么呢?给出该博文的定义:

线段树,是一个完全二叉树,它在各个节点保存一条线段(数组中的一段子数组),主要用于高效解决连续区间的动态查询问题,由于二叉结构的特性,它基本能保持每个操作的复杂度为O(logn)。

线段树的每个节点表示一个区间,子节点则分别表示父节点的左右半区间,例如父亲的区间是[a,b],那么(c=(a+b)/2)左儿子的区间是[a,c],右儿子的区间是[c+1,b]。


也许看过树状数组的同学会有印象,树状数组里面的c[i]表示的是1~i区间的总和。

于是可以将线段树看做是树状数组的升级版。


来了解一下线段树主要想解决什么问题:

问题描述如下:从数组arr[0...n-1]中查找某个数组某个区间内的最小值,其中数组大小固定,但是数组中的元素的值可以随时更新。

对这个问题一个简单的解法是:遍历数组区间找到最小值,时间复杂度是O(n),额外的空间复杂度O(1)。当数据量特别大,而查询操作很频繁的时候,耗时可能会不满足需求。


比如:-1,3,4,0,2,1 .现在我希望知道2~4区间里的最小值

传统的自然是直接求取,要么是使用一个二维数组存储所有区间的值。

当区间内有元素更新了之后,需要对整个二维数组进行更新,效率比较有限。

于是线段树就是为了解决这一类问题;

让我们来看一下线段树长什么样子:


可以看到线段树的每个节点保存了一个区间的最小值,当我们需要查询一个区间的最小值的时候,将目标区间从根节点开始下降,有3种情况:

  1. 部分覆盖    --->返回min(left,right) 其中left和right分别为左右子树所返回的值
  2. 完全覆盖    --->返回root,表明当前区间在某个节点内
  3. 未覆盖       ---> 返回INT_MAX,这个值将在递归的过程中由min函数进行舍弃

故考虑区间[0,3]

根节点部分覆盖,下降到左右孩子节点

[0,3]完全覆盖左子树节点,返回-1,

[0,3]部分覆盖右子树,继续判断左右子树

[0,3]和[5,5]不覆盖,返回INT_MAX

......

于是可以在O(lgN)时间内求出最小值。


于是如何将一个数组转化为一个线段树呢?

由于线段树是一棵完全二叉树,所以也可以用数组表示!!

我们看以下代码

void constructTree(int input[],int segTree[],int low,int high,int pos){if(low==high){segTree[pos]=input[low];return;}//二分 int mid=(low+high)/2;//递归构造左右子树 constructTree(input,segTree,low,mid,2*pos+1);constructTree(input,segTree,mid+1,high,2*pos+2);//根据左右子树的值回溯更新当前节点的值segTree[pos]=min(segTree[2*pos+1],segTree[2*pos+2]);}

代码比较简单,而且我们利用了一个重要的性质:对于一个完全二叉树,左孩子编号=2*root编号+1 右孩子编号=2*root编号+2

那么如何求一个区间的最小值呢?

于是我们采取和前面推导过程类似的递归方法,分为3种情况进行递归:

int rangeMinQuery(int segTree[],int qlow,int qhigh,int low,int high,int pos) {if(qlow<low && qhigh>high){//完全覆盖 return segTree[pos];}if(qlow>high || qhigh<low){//未覆盖 return INT_MAX;}//部分覆盖 int mid=(low+high)/2;return min(rangeMinQuery(segTree,qlow,qhigh,low,mid,2*pos+1),rangeMinQuery(segTree,qlow,qhigh,mid+1,high,2*pos+2));}


应用:

待续....













0 0
原创粉丝点击