线段树入门整理、

线段树(interval tree) 是把区间逐次二分得到的一树状结构,它反映了包括归并排序在内的很多分治算法的问题求解方式。

【声明】

1 #include<cstdio>
2 #include<cmath>
3 const int MAXNODE = 2097152;
4 const int MAX = 1000003;
5 struct NODE{
6     int left,right;    // 区间[left,right] 、
7     int value;        // 节点区间对应的权值、
8 }node[MAXNODE];
9 int father[MAX];   // 每个点(当区间长度为0时,对应一个点)对应的结构体数组下标

【创建线段树(初始化)】:

线段树是用二叉树结构存储的,而且是完全二叉树

在完全二叉树中假如一个结点的序号(数组下标)为 n ,那么 (二叉树基本关系)

n的父亲为 n/2,

n 的另一个兄弟为 n/2*2 或 n/2*2+1

n 的两个孩子为n*2 (左)   n*2+1(右)

根据这样的关系,就可以很简单明了的写出建树的代码了

 1 void buildtree(int i,int left,int right)    // 为区间[left,right]建立一个以结点i为祖先的线段树,i又为为数组下标,也是结点序号
 2 {
 3     node[i].left=left;            //写入第i个结点的 左区间 、
 4     node[i].right=right;        //写入第i个结点的 右区间 、
 5     node[i].value=0;            // 对每个区间进行初始化 、
 6     if(left==right){            // 当区间长度为0的时候,结束递归、
 7         father[left]=i;            //能知道某个点对应的序号,为了更新的时候从下往上一直到顶,也就是保存left对应结点i在结构体数组中的位置
 8         return;
 9     }
10     // 该结点往 左孩子的方向 继续建立线段树
11    // 这里将 区间[left,right] 一分为二了
12      buildTree(i<<1, left, (right+left) / 2));
13      // 该结点往 右孩子的方向 继续建立线段树
14      buildTree((i<<1)+1, (right+left) / 2 + 1, right);
15 } 

【单点更新线段树】:

最初用father数组保存了每一个单位区间长度的结点下标,因此就容易由下往上更新了

(此处以更新区间最大值为例)

 1 void updatatree(int ri)    //从下往上更新(这个点本身已经在函数外面更新过了)
 2 {
 3     if(ri==1)    return;        // 已经找到整个树的根节点了,结束递归
 4     int fi = ri / 2;        // ri的父结点
 5     //该父结点的两个孩子
 6     int a = node[fi<<1].value;
 7     int b = node[(fi<<1)+1].value;
 8     node[fi].value=(a>b)?(a):(b);    //更新这个父结点
 9     updatatree(ri/2);        // 继续递归,由父结点往上找
10 }

【查询区间最大值】:

将一段区间按照建立的线段树从上往下一直拆开,直到存在有完全重合的区间停止。对照图例建立的树,假如查询区间为 [2,5]

红色的区间为完全重合的区间,因为在这个具体问题中我们只需要比较这 三个区间的值 找出 最大值 即可。

 1 int Max = -1 << 20;
 2 void query(int i,int l,int r)
 3 {
 4     if(node[i].left==l && node[i].right==r){            // 找到一个完全重合区间
 5         Max = (Max < node[i].value)?node[i].value:(Max);
 6         return;
 7     }
 8     i = i << 1;             //查找此结点的左孩子结点
 9     if(l <= node[i].right){        // 左区间有涉及
10         if(r<=node[i].right)    // 全包含于左区间,则查询区间形态不变
11             query(i,l,r);
12         else                    // 半包含于左区间,则查询区间拆分,左端点不变右端点变为左孩子的右区间
13             query(i,l,node[i].right);
14     }
15     i+=1;        //右孩子结点
16     if(r >= node[i].left){        //右区间有涉及
17         if(l >= node[i].left)    //全包含于右区间,查询状态不变
18             query(i,l,r);
19         else                    // 半包含于左区间,查询区间拆分
20             query(i,node[i].left,r);
21     }
22 }    
时间: 2024-08-07 17:01:14

线段树入门整理、的相关文章

线段树入门小结

QUE:线段树? 称谓: 从刘汝佳的书中得知,"这种数据结构在学术界没有统一的术语,但线段树是最常见的叫法.其他叫法包括区间树(interval tree).范围树(range tree)等,但这些属于在特定的场合(如计算几何)中有着特殊的意义".怎么叫看读者的心情,以下统一用线段树称呼. 先来作一些了解: 线段树是一棵二叉树,它的左右儿子也都是一棵线段树.(定义) 线段树也叫区间树,为什么叫它区间树呢?因为线段树是一种基于区间的数据结构. 线段树的每个节点代表一个区间 [L,R],其

线段树入门(Billboard)

Billboard Time Limit:8000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Description At the entrance to the university, there is a huge rectangular billboard of size h*w (h is its height and w is its width). The board is the place where

线段树入门(I Hate It)

I Hate It Time Limit:3000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Description 很多学校流行一种比较的习惯.老师们很喜欢询问,从某某到某某当中,分数最高的是多少. 这让很多学生很反感. 不管你喜不喜欢,现在需要你做的是,就是按照老师的要求,写一个程序,模拟老师的询问.当然,老师有时候需要更新某位同学的成绩. Input 本题目包含多组测试,请处理到文件结束. 在每个测试的第一行,

《数据结构》线段树入门(二)

今天继续介绍——线段树之延迟标记 接上期<数据结构>线段树入门(一):http://www.cnblogs.com/shadowland/p/5870339.html 在上期介绍了线段树的最基本内容(线段树单点修改,区间查询),这次将介绍:区间修改,区间查询. Question: 给你N个数,有两种操作: 1:给区间[a,b]的所有数增加X 2:询问区间[a,b]的数的和. 输入描述: 第一行一个正整数n,接下来n行n个整数,再接下来一个正整数Q,每行表示操作的个数,如果第一数是1,后接3个正

线段树入门理解

在复习算法至分治法时,书本上主要介绍了合并排序和快速排序,较为简单.特拓展简单学习一个应用了分治法的算法结构--线段树. acm刷题时遇到许多连续区间的动态查询问题,例如求取某一区间上元素之和.求取某一区间上元素的最大值,此时如果使用一般的方法求解会使得时间超出要求.此时需要使用到线段树,其主要用于高效解决连续区间的动态查询问题. 线段树,类似区间树,是一个完全二叉树,它在各个节点保存一条线段(数组中的一段子数组),由于二叉结构的特性,它基本能保持每个操作的复杂度为O(lgN),从而大大减少耗时

hdu1166敌兵布阵&amp;&amp;hdu1754I Hate It(线段树入门)

单点更新是最最基础的线段树,只更新叶子节点,然后把信息用pushup这个函数更新上来. http://acm.hdu.edu.cn/showproblem.php?pid=1166 update单点更新,query区域求和. #include <iostream> #include <stdio.h> #include <string.h> #include <stdlib.h> #define N 200001 using namespace std; s

hdu 1754 I Hate It(线段树入门)

I Hate It Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 41510    Accepted Submission(s): 16458 Problem Description 很多学校流行一种比较的习惯.老师们很喜欢询问,从某某到某某当中,分数最高的是多少. 这让很多学生很反感. 不管你喜不喜欢,现在需要你做的是,就是按照老师

线段树入门(更新单个节点)

很多学校流行一种比较的习惯.老师们很喜欢询问,从某某到某某当中,分数最高的是多少. 这让很多学生很反感. 不管你喜不喜欢,现在需要你做的是,就是按照老师的要求,写一个程序,模拟老师的询问.当然,老师有时候需要更新某位同学的成绩. Input本题目包含多组测试,请处理到文件结束. 在每个测试的第一行,有两个正整数 N 和 M ( 0<N<=200000,0<M<5000 ),分别代表学生的数目和操作的数目. 学生ID编号分别从1编到N. 第二行包含N个整数,代表这N个学生的初始成绩,

线段树入门---给定多个线段求点的出现个数

线段树是一颗二叉树,他的每个节点都是一个区间,此题为线段树的入门题目,只是学习笔记.例题:给定N个线段,给定M个点,求点在多少个线段中出现过,此时如果用传统的方法来求,时间复杂度太高,但是,线段树的时间复杂度还可以接受. 步骤为: 1. 首先找一个区间,能覆盖给定的所有区间, 然后把此区间建立线段树,建立线段树的方式是二分法建立,即它的左孩子是他的左半个区间,右孩子是它的右边那个区间.一个图足以说明清楚 2. 将所有的区间映射到此树上, 从根节点开始遍历, 每遍历一个节点考虑四种情况: 1) 当