线段树

来源:互联网 发布:情定三生知夏向天圆房 编辑:程序博客网 时间:2024/06/05 10:03

白书上写的 基于线段树的RMQ实现讲解的有那么点绕。 

然后这篇文章把每一步都详细的解读了:

http://www.cnblogs.com/zqy123/p/4899197.html

我自己写的板子,上面博客中有一处地方是错的我觉得

const int inf = 0x3f3f3f3f;int n;// n是数组个数int tree[maxn];void init(){    int k = 1;//k是满足大于2^n中n的最小节点数,也可以想象为该平衡二叉树最下面一层的节点数,那么整棵树就是2^k-1    while(k < n)        k <<= 1;    for(int i=1; i<=2*k-1; i++)       tree[i] = inf;//因为是不断向上更新最小值,所以把他设置成无限大就可以了}void update(int pos, int val) //pos是数组下标,{    pos += n-1; // 现在的pos就变成树的下标了 比如原来pos是1,n是8,也就是4层,现在就变成8正好是第四层第一个    tree[pos] = val;    while(pos >= 1)    {        pos/=2;        tree[pos] = min(tree[pos*2],tree[pos*2+1]);    }}



然后这是线段树查询的板子

//初始时输入(a,b,1,1,n)//a,b是需要查询的区间,pos是树结点的下标,l,r是当前树结点维护的区间。int query(int a, int b, int pos, int l, int r){        if(b<=l||a>=r) return INF;//完全不相交返回一个很大的数表示不存在        if(a<=l && r<=b) return tree[pos];//返回当前树结点的维护的最小值 完全包含在内        else //否则返回两个儿子中的最小值,递归来表示        {            int vl = query(a, b, pos*2, int l , int (l+r)/2);            int vr = query(a, b, pos*2+1, int (l+r)/2+1, int r)            return min(vl,vr);        }}


好几种写法:

const int MAXNUM = 1000;struct SegTreeNode{    int val;}segTree[MAXNUM];//定义线段树/*功能:构建线段树root:当前线段树的根节点下标arr: 用来构造线段树的数组istart:数组的起始位置iend:数组的结束位置*/void build(int root, int arr[], int istart, int iend){    if(istart == iend)//叶子节点        segTree[root].val = arr[istart];    else    {        int mid = (istart + iend) / 2;        build(root*2+1, arr, istart, mid);//递归构造左子树        build(root*2+2, arr, mid+1, iend);//递归构造右子树        //根据左右子树根节点的值,更新当前根节点的值        segTree[root].val = min(segTree[root*2+1].val, segTree[root*2+2].val);    }}

基于RMQ的ST算法择日学习,因为遇到一到练习题POJ3368用到的便是这个。


终于有机会学习ST了,实质是个dp ,隔了整整2个月。。。

然后这是ST算法的板子:

/**构造RMQ数组 makermq(int n,int b[]) O(nlog(n))的算法复杂度*dp[i][j] 表示从i到i+2^j -1中最小的一个值(从i开始持续2^j个数)*dp[i][j]=min{dp[i][j-1],dp[i+2^(j-1)][j-1]}*查询RMQ rmq(int s,int v)*/void makermq(int n,int b[]){    int i,j;    for(i=0;i<n;i++)        dp[i][0]=b[i];    for(j=1;(1<<j)<=n;j++)        for(i=0;i+(1<<j)-1<n;i++)            dp[i][j]=min(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);}int rmq(int s,int v){    int k=(int)(log((v-s+1)*1.0)/log(2.0));    return min(dp[s][k],dp[v-(1<<k)+1][k]);}int main(){    int a[]={3,4,5,7,8,9,10,3,4,5};    //返回最小值    makermq(10,a);    cout<<rmq(0,9)<<endl;    cout<<rmq(4,9)<<endl;    return 0;}
假设我们要求区间[m,n]中a的最小值,找到一个数k使得2^k<n-m+1. 
这样,可以把这个区间分成两个部分:[m,m+2^k-1]和[n-2^k+1,n].我们发现,这两个区间是已经初始化好的. 
前面的区间是f(m,k),后面的区间是f(n-2^k+1,k). 

这是从一篇文章那看来的,讲的很清楚了,包括了ST和线段树求解RMQ问题的程序。原链接:http://kmplayer.iteye.com/blog/575725


1 0
原创粉丝点击