@helen-
2017-02-27T11:42:47.000000Z
字数 1746
阅读 1346
算法
线段树,类似区间树,它在各个节点保存一条线段(数组中的一段子数组),主要用于高效解决连续区间的动态查询问题,由于二叉结构的特性,它基本能保持每个操作的复杂度为O(logn)。
线段树的每个节点表示一个区间,子节点则分别表示父节点的左右半区间,例如父亲的区间是[a,b],那么(c=(a+b)/2)左儿子的区间是[a,c],右儿子的区间是[c+1,b]。
例如对于数组[2, 5, 1, 4, 9, 3]可以构造如下的二叉树(背景为白色表示叶子节点,非叶子节点的值是其对应数组区间内的最小值,例如根节点表示数组区间arr[0...5]内的最小值是1):

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);}}
/*功能:线段树的区间查询root:当前线段树的根节点下标[nstart, nend]: 当前节点所表示的区间[qstart, qend]: 此次查询的区间*/int query(int root, int nstart, int nend, int qstart, int qend){//查询区间和当前节点区间没有交集if(qstart > nend || qend < nstart)return INFINITE;//当前节点区间包含在查询区间内if(qstart <= nstart && qend >= nend)return segTree[root].val;//分别从左右子树查询,返回两者查询结果的较小值int mid = (nstart + nend) / 2;return min(query(root*2+1, nstart, mid, qstart, qend),query(root*2+2, mid + 1, nend, qstart, qend));}
/*功能:更新线段树中某个叶子节点的值root:当前线段树的根节点下标[nstart, nend]: 当前节点所表示的区间index: 待更新节点在原始数组arr中的下标addVal: 更新的值(原来的值加上addVal)*/void updateOne(int root, int nstart, int nend, int index, int addVal){if(nstart == nend){if(index == nstart)//找到了相应的节点,更新之segTree[root].val += addVal;return;}int mid = (nstart + nend) / 2;if(index <= mid)//在左子树中更新updateOne(root*2+1, nstart, mid, index, addVal);else updateOne(root*2+2, mid+1, nend, index, addVal);//在右子树中更新//根据左右子树的值回溯更新当前节点的值segTree[root].val = min(segTree[root*2+1].val, segTree[root*2+2].val);}