算法导论学习-线段树(2)

线段树(1)http://www.cnblogs.com/fu11211129/p/4230000.html

1. 线段树应用之动态点插与统计:

--------------------------------

线段树(1)中讲的应用是区段的插值与统计,我们在线段树结构体中接入cover之一域,cover等于0表示该节点所代表的区域并没有被完全覆盖,cover大于等于1表示该节点所代表区域已经被完全覆盖,用线段树(1)博客里面的图来说明一下:

         

如上图所示,我们最后统计的时候是找出cover不为零的节点,(再上图对应的是节点[3,4]. [5,6], [2], [4], [5], [7].),但是注意到[3,4]和[4] 还有[5,6]和[5]不能从重复统计,所以最后统计的长度是[2]+[3,4]+[5,6]+[7]=6.所以四根木条在墙上的总投影长是6.

现在我们可以来归纳一下区段插值的几个重要部分:首先是插入动作,比如插入[L, R]线段,插入动作需要递归的从根节点开始插入,而递归的出口就是找到能够和[L, R]完全匹配的线段树节点,即找到a[i].left==L AND a[i].right==R,并且同时将a[i].cover值加1.,递归终止。然后是删除动作,假设删除[L, R],同样的也是要从根节点开始删除,也是要递归的找到与[L, R]完全匹配的线段树节点a[i],并且将a[i].cover值减去1.最后是统计,仍然是从根节点开始递归的统计,递归的出口要么是找到的节点a[i]满足a[i].cover>=0,则返回该节点代表的区段长度。要么是找到叶子节点(a[i].left==a[i].right),如果叶子节点的cover不为0,则返回1,否则返回零。

以上是对区段插入的回顾,下面开始介绍动态点插与统计。所谓动态点插(其实是我自己DIY的,所以我就用图表示一下吧,大家理解就好)

如上图所示,1-8表示相互连接的8个槽,然后在这些草内的任意位置放置任意数量个小球,然后让你求出任意区段内[L, R]的小球数量。比如[2,3]区段内的小球数量是2,[1,6]区段内的小球数量是6。要解决这个问题,可以优良中办法,一种方法不用说也知道,暴力嘛,开一个数组A,然后更新数组的值。。这种做法当“槽”的范围过大时是效率很低的。所以这时候线段树再次派上用场了。我们还是用cover域来表示某个节点缩地阿彪范围的小球数量。比如根节点代表1-8槽的小球数量。

###第一步,建立线段树(http://www.cnblogs.com/fu11211129/p/4230000.html 里面有介绍,这里直接跳过)

###第二步,线段树插入(点插)

1 void updatePoint(int pos,int step){
2     a[step].num++;
3     if(a[step].left==a[step].right&&a[step].left==pos) return;//leaf node
4     int mid=(a[step].left+a[step].right)/2;
5     if(pos<=mid) updatePoint(pos,step*2);
6     else updatePoint(pos,step*2+1);
7 }

我们可以看到,线段树点插动作是“一路插值的”,这也不难理解,比如我在第3个槽内放置一个球,那么显然的,节点[3].num=1,但同时的,节点[3,4],[1,2,3,4],[1,2,3,4,5,6,7,8]的num值也是为1.所以线段树点插可以理解记忆为:一路递归向下插值。

###第三步,线段树统计

 1 int query(int L,int R,int step){
 2     if(L==a[step].left&&R==a[step].right){
 3         return a[step].num;
 4     }
 5     else{
 6         int mid=(a[step].left+a[step].right)/2,ans=0;
 7         if(R<=mid) ans+=query(L,R,step*2);
 8         else if(L>=mid+1) ans+=query(L,R,step*2+1);
 9         else{
10             ans+=query(L,mid,step*2)+query(mid+1,R,step*2+1);
11         }
12         return ans;
13     }
14 }

可以看到,线段树点插结果统计是按照区段来查询的,所以递归查询的出口也就是找到与之匹配的节点,然后返回相应的cover值,对应代码2-4行。如果与当前节点不匹配,则递归的“往下”查[6-8],如果带查询区段在左右子树都有子结果,则返回两个子结果之和。

2. 线段树应用之区间最大最小值问题:

-----------------------------------

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 using namespace std;
 5 const int max_size=100;
 6 const int inf=1<<30;
 7 struct segmentTree{
 8     int lc,rc,mi,mx;
 9 }a[max_size*3];
10 void buildTree(int L,int R,int rt){
11     a[rt].lc=L;
12     a[rt].rc=R;
13     a[rt].mi=inf;
14     a[rt].mx=-inf;
15     if(L==R) return;
16     int mid=(a[rt].lc+a[rt].rc)/2;
17     buildTree(L,mid,rt*2);
18     buildTree(mid+1,R,rt*2+1);
19 }
20 void insert(int pos,int v,int rt){
21     if(pos==a[rt].lc&&a[rt].lc==a[rt].rc){
22         a[rt].mi=v;
23         a[rt].mx=v;
24         return;
25     }
26     int mid=(a[rt].lc+a[rt].rc)/2;
27     if(pos<=mid) insert(pos,v,rt*2);
28     else insert(pos,v,rt*2+1);
29     a[rt].mi=min(a[rt*2].mi,a[rt*2+1].mi);
30     a[rt].mx=max(a[rt*2].mx,a[rt*2+1].mx);
31 }
32 int query_min(int L,int R,int rt){
33     if(L==a[rt].lc&&R==a[rt].rc) return a[rt].mi;
34     if(a[rt].lc<a[rt].rc){
35         int mid=(a[rt].lc+a[rt].rc)/2,ret;
36         if(R<=mid) ret=query_min(L,R,rt*2);
37         else if(L>=mid+1) ret=query_min(L,R,rt*2+1);
38         else ret=min(query_min(L,mid,rt*2),query_min(mid+1,R,rt*2+1));
39         return ret;
40     }
41 }
42 int main(){
43     int n,m,q,p,v;
44     while(scanf("%d",&n)!=EOF){
45         buildTree(1,n,1);
46         scanf("%d",&m);
47         for(int i=1;i<=m;i++){
48             scanf("%d%d",&p,&v);
49             insert(p,v,1);
50         }
51         scanf("%d",&q);
52         while(q--){
53             int a,b;
54             scanf("%d%d",&a,&b);
55             printf("%d\n",query_min(a,b,1));
56         }
57     }
58 }
时间: 2024-10-09 20:20:45

算法导论学习-线段树(2)的相关文章

算法导论学习-线段树(1)

1. 线段树简介: --------------- 线段树是建立在线段的基础上,每个结点都代表了一条线段[a,b].长度为1的线段称为元线段.非元线段都有两个子结点,左结点代表的线段为[a,(a + b) / 2],右结点代表的线段为[((a + b) / 2)+1,b].若非元线段的编号为 i,那么他的左孩子编号为 2*i,右孩子为 2*i+1.下图是一棵典型的线段树.,一般的,我们把根节点的编号设为1,那么下图很显然的,[1,5]的编号为2,[6,10]的编号为3,其他节点也以此类推. 2.

算法导论学习---红黑树具体解释之插入(C语言实现)

前面我们学习二叉搜索树的时候发如今一些情况下其高度不是非常均匀,甚至有时候会退化成一条长链,所以我们引用一些"平衡"的二叉搜索树.红黑树就是一种"平衡"的二叉搜索树,它通过在每一个结点附加颜色位和路径上的一些约束条件能够保证在最坏的情况下基本动态集合操作的时间复杂度为O(nlgn).以下会总结红黑树的性质,然后分析红黑树的插入操作,并给出一份完整代码. 先给出红黑树的结点定义: #define RED 1 #define BLACK 0 ///红黑树结点定义,与普通

算法导论学习---红黑树详解之插入(C语言实现)

前面我们学习二叉搜索树的时候发现在一些情况下其高度不是很均匀,甚至有时候会退化成一条长链,所以我们引用一些"平衡"的二叉搜索树.红黑树就是一种"平衡"的二叉搜索树,它通过在每个结点附加颜色位和路径上的一些约束条件可以保证在最坏的情况下基本动态集合操作的时间复杂度为O(nlgn).下面会总结红黑树的性质,然后分析红黑树的插入操作,并给出一份完整代码. 先给出红黑树的结点定义: #define RED 1 #define BLACK 0 ///红黑树结点定义,与普通的二

【算法导论学习-015】数组中选择第i小元素(Selection in expected linear time)

1.算法思想 问题描述:从数组array中找出第i小的元素(要求array中没有重复元素的情况),这是个经典的"线性时间选择(Selection in expected linear time)"问题. 思路:算法导论215页9.2 Selection in expect linear time 2.java实现 思路:算法导论216页伪代码 /*期望为线性时间的选择算法,输入要求,array中没有重复的元素*/ public static int randomizedSelect(i

算法导论 学习资源

学习的过程会遇到些问题,发现了一些比较好的资源,每章都会看下别人写的总结,自己太懒了,先记录下别人写的吧,呵呵. 1  Tanky Woo的,每次差不多都看他的 <算法导论>学习总结 - 1.前言 <算法导论>学习总结 - 2.第一章 && 第二章 && 第三章 <算法导论>学习总结 - 3.第四章 && 第五章 <算法导论>学习总结 - 4.第六章(1) 堆排序 <算法导论>学习总结 - 5.第六

【算法导论学习-014】计数排序(CountingSortTest)

参考:<算法导论>P194页 8.2节 Counting sort 1.Counting sort的条件 待排序数全部分布在0~k之间,且k是已知数:或者分布在min~max之间,等价于分布在0~max-min之间,max和min是已知数. 2.java 实现 /** * 创建时间:2014年8月17日 下午3:22:14 项目名称:Test * * @author Cao Yanfeng * @since JDK 1.6.0_21 类说明: 计数排序法,复杂度O(n), 条件:所有数分布在0

【算法导论学习-015】基数排序(Radix sort)

1.<算法导论>P197页 8.3节Radix sort 2.java实现 这里仅仅对[算法导论学习-014]计数排序 的参数进行了修改,同时仅仅修改了一行代码. /** * 创建时间:2014年8月17日 下午4:05:48 * 项目名称:Test * @author Cao Yanfeng * @since JDK 1.6.0_21 * 类说明: 利用计数排序实现基数排序 * 条件:待排序的所有数位数相同,注意,即便不相同,也可以认为是最多那个位数,如下面的例子可以认为都是3位数 */ p

【算法导论学习-016】两个已排过序的等长数组的中位数(median of two sorted arrays)

问题来源 <算法导论>P223 9.3-8: Let X[1..n] and Y[1..n] be two arrays, each containing nnumbers already in sorted order. Give an O(lgn)-time algorithm to find themedian of all 2n elements in arrays X and Y. 翻译过来即:求两个等长(n个元素)的已排序数组A和B的中位数 方案1:对两个数组进行归并直到统计到第n

【算法导论学习-012】n个数随机等概率的抽样m个

算法法导论>P129页课后题5.3-7 suppose we want to create a random sample of the set {1,2,3,-,n}, thatis, an m-element subset S, where0≤m≤n, such that each m-subset is equally likely to be created. One waywould be to set A[i]=i for i=1,2,3,-,n, call RANDOMIZE-IN