[关闭]
@helen- 2017-02-27T11:42:47.000000Z 字数 1746 阅读 1175

学习线段树

算法


概念

线段树,类似区间树,它在各个节点保存一条线段(数组中的一段子数组),主要用于高效解决连续区间的动态查询问题,由于二叉结构的特性,它基本能保持每个操作的复杂度为O(logn)。
线段树的每个节点表示一个区间,子节点则分别表示父节点的左右半区间,例如父亲的区间是[a,b],那么(c=(a+b)/2)左儿子的区间是[a,c],右儿子的区间是[c+1,b]。

举栗子

例如对于数组[2, 5, 1, 4, 9, 3]可以构造如下的二叉树(背景为白色表示叶子节点,非叶子节点的值是其对应数组区间内的最小值,例如根节点表示数组区间arr[0...5]内的最小值是1):

线段树

由于线段树的父节点区间是平均分割到左右子树,因此线段树是完全二叉树,对于包含n个叶子节点的完全二叉树,它一定有n-1个非叶节点,总共2n-1个节点,因此存储线段是需要的空间复杂度是O(n)。

算法

  1. const int MAXNUM = 1000;
  2. struct SegTreeNode
  3. {
  4. int val;
  5. }segTree[MAXNUM];//定义线段树
  6. /*
  7. 功能:构建线段树
  8. root:当前线段树的根节点下标
  9. arr: 用来构造线段树的数组
  10. istart:数组的起始位置
  11. iend:数组的结束位置
  12. */
  13. void build(int root, int arr[], int istart, int iend)
  14. {
  15. if(istart == iend)//叶子节点
  16. segTree[root].val = arr[istart];
  17. else
  18. {
  19. int mid = (istart + iend) / 2;
  20. build(root*2+1, arr, istart, mid);//递归构造左子树
  21. build(root*2+2, arr, mid+1, iend);//递归构造右子树
  22. //根据左右子树根节点的值,更新当前根节点的值
  23. segTree[root].val = min(segTree[root*2+1].val, segTree[root*2+2].val);
  24. }
  25. }
  1. /*
  2. 功能:线段树的区间查询
  3. root:当前线段树的根节点下标
  4. [nstart, nend]: 当前节点所表示的区间
  5. [qstart, qend]: 此次查询的区间
  6. */
  7. int query(int root, int nstart, int nend, int qstart, int qend)
  8. {
  9. //查询区间和当前节点区间没有交集
  10. if(qstart > nend || qend < nstart)
  11. return INFINITE;
  12. //当前节点区间包含在查询区间内
  13. if(qstart <= nstart && qend >= nend)
  14. return segTree[root].val;
  15. //分别从左右子树查询,返回两者查询结果的较小值
  16. int mid = (nstart + nend) / 2;
  17. return min(query(root*2+1, nstart, mid, qstart, qend),
  18. query(root*2+2, mid + 1, nend, qstart, qend));
  19. }
  1. /*
  2. 功能:更新线段树中某个叶子节点的值
  3. root:当前线段树的根节点下标
  4. [nstart, nend]: 当前节点所表示的区间
  5. index: 待更新节点在原始数组arr中的下标
  6. addVal: 更新的值(原来的值加上addVal)
  7. */
  8. void updateOne(int root, int nstart, int nend, int index, int addVal)
  9. {
  10. if(nstart == nend)
  11. {
  12. if(index == nstart)//找到了相应的节点,更新之
  13. segTree[root].val += addVal;
  14. return;
  15. }
  16. int mid = (nstart + nend) / 2;
  17. if(index <= mid)//在左子树中更新
  18. updateOne(root*2+1, nstart, mid, index, addVal);
  19. else updateOne(root*2+2, mid+1, nend, index, addVal);//在右子树中更新
  20. //根据左右子树的值回溯更新当前节点的值
  21. segTree[root].val = min(segTree[root*2+1].val, segTree[root*2+2].val);
  22. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注