1、概述

线段树,也叫区间树,是一个完全二叉树,它在各个节点保存一条线段(即“子数组”),因而常用于解决数列维护问题,基本能保证每个操作的复杂度为O(lgN)。

线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。

对于线段树中的每一个非叶子节点[a,b],它的左儿子表示的区间为[a,(a+b)/2],右儿子表示的区间为[(a+b)/2+1,b]。因此线段树是平衡二叉树,最后的子节点数目为N,即整个线段区间的长度。

使用线段树可以快速的查找某一个节点在若干条线段中出现的次数,时间复杂度为O(logN)。而未优化的空间复杂度为2N,因此有时需要离散化让空间压缩。

本文地址:http://www.cnblogs.com/archimedes/p/segment-tree.html,转载请注明源地址。

2、线段树基本操作

线段树的基本操作主要包括构造线段树,区间查询和区间修改。

(1)线段树构造

首先介绍构造线段树的方法:让根节点表示区间[0,N-1],即所有N个数所组成的一个区间,然后,把区间分成两半,分别由左右子树表示。不难证明,这样的线段树的节点数只有2N-1个,是O(N)级别的,如图:

节点定义如下:

typedef struct node {int l;        //线段的左端点int r;        //线段的左端点int value;    //线段上的值
}node;

线段树的构建

bulid//以节点v为根建树、v对应区间为[l,r]
{对节点v初始化if (l!=r) {以v的左孩子为根建树,区间为[l,(l+r)/2]以v的右孩子为根建树,区间为[(l+r)/2+1,r]}
}

完整的建树代码如下:

#define N 10000
node tree[N];
void bulid(int l, int r, int v) //对结点v进行建立,区间为l~r
{tree[v].l = l;tree[v].r = r;if(l == r) {//进行结点的初始化tree[v].value = a[r];return;}int mid = (l + r) / 2;bulid(v * 2, l, mid);bulid(v * 2 + 1, mid + 1, r);//根据左右儿子更新当前结点tree[v].value = tree[v * 2].value + tree[v * 2 + 1].value;
}

更新

当在a[i]~a[j]上的所有的元素都加上一个值c的时候

如果a[i]~a[j]刚还是一个完整段的时候,直接将这个段的value值加上c*(r-l+1)

当更新的区间不是一个完整段的时候,采用一种记录增量的方法:给每个节点增加一个域:int add,记录更新操作的增量c,初始的时候add均为0,比如当对2~5区间更新后,给该结点的add加上一个值c,再下次要对2~3结点进行更新或查询时,再将add传递到下面的孩子结点中去

完整的更新树代码如下:

typedef struct node {int l;        //线段的左端点int r;        //线段的左端点int value;    //线段上的值int add;
}node;
void update(int v, int r, int l, int m)//更新区间l~r加上数m
{if(tree[v].l == l && tree[v].r == r) {  //找到,更新并记录增量tree[v].value += m * (r - l + 1);tree[v].add = m;return;}if(tree[v].add) {tree[2 * v].add += tree[v].add;tree[2 * v + 1].add += tree[v].add;tree[v].add = 0;}int mid = (tree[v].l + tree[v].r) / 2;if(r <= mid) {update(v * 2, l, r, m);   //只对左儿子更新} else {if(l > mid) {update(v * 2 + 1, l, r, m);  //只对右儿子更新} else {                        //区间横跨左右儿子区间,对其两者均进行更新update(v * 2, l, mid, m);update(v * 2 + 1, mid + 1, r, m);}}
}

查询

查询区间l~r上的value值

void query(int v, int l, int r)  //当前查询结点为v,要查询的区间为l~r
{if(tree[v].l == l && tree[v].r == r) {ans += tree[v].value;return;}if(tree[v].add) {tree[v * 2].add += tree[v].add;tree[v * 2 + 1].add += tree[v].add;tree[v].add = 0;}int mid = (tree[v].l + tree[v].r) / 2;if(r <= mid) {query(v * 2, l, r);   //要查询的区间都在左儿子} else {if(l > mid) {query(v * 2 + 1, l, r);  //要查询的区间都在左儿子} else {                //要查询的区间横跨左右孩子query(v * 2, l, mid);query(v * 2 + 1, mid + 1, r);}}
}

编程实践

poj2182 Lost Cows

DescriptionN (2 <= N <= 8,000) cows have unique brands in the range 1..N. In a spectacular display of poor judgment, they visited the neighborhood 'watering hole' and drank a few too many beers before dinner. When it was time to line up for their evening meal, they did not line up in the required ascending numerical order of their brands. Regrettably, FJ does not have a way to sort them. Furthermore, he's not very good at observing problems. Instead of writing down each cow's brand, he determined a rather silly statistic: For each cow in line, he knows the number of cows that precede that cow in line that do, in fact, have smaller brands than that cow. Given this data, tell FJ the exact ordering of the cows.
Input* Line 1: A single integer, N * Lines 2..N: These N-1 lines describe the number of cows that precede a given cow in line and have brands smaller than that cow. Of course, no cows precede the first cow in line, so she is not listed. Line 2 of the input describes the number of preceding cows whose brands are smaller than the cow in slot #2; line 3 describes the number of preceding cows whose brands are smaller than the cow in slot #3; and so on.
Output* Lines 1..N: Each of the N lines of output tells the brand of a cow in line. Line #1 of the output tells the brand of the first cow in line; line 2 tells the brand of the second cow; and so on.
Sample Input5
1
2
1
0
Sample Output2
4
5
3
1

#include<stdio.h>
#include<stdlib.h>
#define N 10000
int small[N], ans[N];
struct segment {int lc, rc, len;
};
struct segment s[3 * N];
void bulid(int root, int lc, int rc)  /*建树*/
{s[root].lc = lc;s[root].rc = rc;s[root].len = rc - lc + 1;if(rc == lc) return;bulid(2 * root, lc, (lc + rc) / 2);bulid(2 * root + 1, (lc + rc) / 2 + 1, rc);
}
int query(int root, int k)
{s[root].len--;if(s[root].lc == s[root].rc) return s[root].lc;else if(k <= s[2 * root].len) {return query(2 * root, k);} else {return query(2 * root + 1, k - s[2 * root].len);}
}int main()
{int n, i;scanf("%d", &n);for(i = 2; i <=n; i++)scanf("%d", &small[i]);small[1] = 0;bulid(1, 1, n);for(i = n; i >= 1; i--)ans[i] = query(1, small[i] + 1);for(i = 1; i <= n; i++)printf("%d\n", ans[i]);return 0;
}

线段树(Segment Tree)相关推荐

  1. BZOJ.4695.最假女选手(线段树 Segment tree Beats!)

    题目链接 区间取\(\max,\ \min\)并维护区间和是普通线段树无法处理的. 对于操作二,维护区间最小值\(mn\).最小值个数\(t\).严格次小值\(se\). 当\(mn\geq x\)时 ...

  2. C语言实现段树segment tree(附完整源码)

    C语言实现段树segment tree 段树结构体定义 实现以下6个接口 完整实现和main测试源码 段树结构体定义 typedef struct segment_tree {void *root; ...

  3. 【数据结构】线段树(interval tree)

    线段树(interval tree),也叫区间树.也是一种二叉搜索树,同一般的BST不同之处在于:线段树的每一个结点包含的是一个区间而不是一个数.具体的描述如下: 从图上可以看出,线段树的每一个结点都 ...

  4. 数据结构线段树介绍与笔试算法题-LeetCode 307. Range Sum Query - Mutable--Java解法

    此文首发于我的个人博客:zhang0peter的个人博客 LeetCode题解文章分类:LeetCode题解文章集合 LeetCode 所有题目总结:LeetCode 所有题目总结 线段树(Segme ...

  5. 线段树segment_tree go语言实现

    线段树(segment tree) 什么是线段树 线段树是一种二叉搜索树,什么叫做二叉搜索树,首先满足二叉树,每个结点度小于等于二,即每个结点最多有两颗子树,何为搜索,我们要知道,线段树的每个结点都存 ...

  6. LintCode 207. 区间求和 II(线段树)

    1. 题目 在类的构造函数中给一个整数数组, 实现两个方法 query(start, end) 和 modify(index, value): 对于 query(start, end), 返回数组中下 ...

  7. 『线段树及扫描线算法 Atlantis』

    入门看这边『线段树 Segment Tree』. 扫描线 扫描线是一种解决一类平面内统计问题的算法,通常会借助线段树来实现,我们通过一道例题来引入这个算法. Atlantis Description ...

  8. l2-004 这是二叉搜索树吗? (25分)_什么是 “线段树” ?

    线段树是一个复杂的数据结构,比较难理解,也比较难解释清楚.在我将这个数据结构反复学习了五遍的时候,我终于有了信心写出这篇介绍线段树的文章.希望大家能够掌握这种数据结构. 这篇文章比较长,建议大家耐心阅 ...

  9. SDUTOJ3771_数组计算机(线段树)

    数组计算机 Time Limit: 1000 ms Memory Limit: 65536 KiB Submit Statistic Problem Description bLue 有一个神器的机器 ...

最新文章

  1. 【机器视觉】 dev_set_paint算子
  2. DB-Engines 9月数据库排名:ClickHouse一路猛冲,Redis坐稳第7
  3. Timer lock
  4. 示例化讲解RIP路由更新机制
  5. NoSQL和Redis简介及Redis在Windows下的安装和使用教程
  6. Sublime Text 支持GBK , 解决中文乱码问题
  7. sh脚本异常:/bin/sh^M:bad interpreter: No such file ...
  8. 棋牌游戏开发教程系列:游戏服务器框架搭建
  9. win10系统恢复win7的照片查看器
  10. .强力卸载或者删除文件
  11. 妙味课堂:JavaScript初级--第11课:字符串、查找高亮显示
  12. sql server使用DAC连接查询系统表
  13. [Vue-Treeselect Warning] Unloaded branch node detected. “loadOptions“ prop is required to load its c
  14. Portainer -- Docker可视化管理工具的安装配置及使用
  15. Arduino使用敲击模块和光遮断
  16. 偶然发现,Javascript中双重否定的写法
  17. php一张纸对折,给你一张纸,你最多能对折几次?
  18. 推荐一招适合新手做网站引流量的方法,方法简单但实际有效
  19. 3天学完10套Python顶级教程,端午节技术人消失之谜
  20. matlab因子载荷矩阵正交旋转,因素分析中的矩阵旋转

热门文章

  1. TRF7970A 天线
  2. 【推荐】JSON在线格式化工具
  3. 加拿大第二大行TD Bank是如何践行科技战略的?
  4. 如何使用@vue/cli 3.0在npm上创建,发布和使用你自己的Vue.js组件库
  5. Docx模板引擎示例
  6. HTTP 之 HTTPD介绍
  7. Atitit.实现反向代理(1)----url rewrite 配置and内容改写 and -绝对路径链接改写 java php...
  8. Linux的远程登陆
  9. rails3高端observer模式
  10. 15-1 并发版爬虫架构