议题:线段树(Interval Tree)

分析:

  • 线段树是一种二叉搜索树,将一个大区间划分成单元区间,每个单元区间对应一个叶子节点;内部节点对应部分区间,如对于一个内部节点[a, b]而言,其左子节点表示的区间为[a, (a+b)/2],其右子节点表示的区间为[1+(a+b)/2, b];

  • 对于区间长度为N的线段树,由于其单元节点都是[a, a]的叶子节点,所以其叶子节点数为N,并且整棵树为平衡二叉树,所以总节点数为2N-1,树的深度为log(N)+1;

  • 插入操作:将一条线段[a, b]插入到区间长度为[l, r]的线段树root中,如果root不是单元节点,则求得mid=(l+r)/2;(删除和查找操作类似)

    如果b<=mid,则将[a, b]插入到root->left子树中;
    如果a=b=mid,则将[a, b]插入到root->left子树中,
    如果a=mid<b,则将[a, a]插入到root->left子树中,[a+1, b]插入到root->right子树中;
    如果a>mid,则将[a, b]插入到root->right子树中;
    如果a<mid<b,则将[a, mid]插入到root->left子树,将[mid+1, b]插入到root->right子树中

  • 线段树最主要的应用是判定几个给定区间之间的关系,判定某一个区间A是否在若干个目标区间内出现,时间复杂度为O(MlogN),M为A的区间长度,N为 构建线段树的整个区间长度;但原始的线段树需要表示每一个单元区间,所以空间复杂度较高为2N,优化方案是离散化(Discretization)压缩线 段树区间;但是由于线段树每个节点上需要维护一个int的计数变量(记录其子树被覆盖的次数),所以每次插入或者删除操作都需要O(N)的时间维护线段树 的正确性,可以为每一个节点增加一个延迟标记的delta值(Delay Mark),这个值记录当前节点所在的区间需要进行的修改(但是还没有对其左右子树的节点进行修改),当查询路径需要到其左右子树中时,将这个delta 值传递给其左右子树,而将本节点保存饿delta值去除;

  • 一道利用线段树的题目:给定一个自然数集合,自然数的范围是[0,30000],现在已知N条线段,每条线段以[a,b]的形式给出,现在有M个数字,要求判断每个数字分别在多少条线段上出现过;

  • 一般解法是将M个数字分别于N条线段比较判断,时间复杂度为O(MN);可以利用线段树记录N条线段在[0, 30000]范围内出现的次数和位置,然后利用二叉树的O(logN)查找时间判断每个数字最终出现的线段;

  • 首先是按照给定的元素范围构建线段树的框架;如给定[0, 7],则利用线段树为满二叉树的性质,其节点个数为2N-1=15;基本存储结构为数组,如果当前节点为array[i],则array[2*i]和 array[2*i+1]分别为左右子节点;节点内拥有一个记录本节点代表的线段出现的次数,初始化为0;时间复杂度为O(logK),K为给定的区间范 围;

  • 然后将给定的N条线段插入到已经构建好的线段树框架中;如给定三条线段:[2,5],[4,6],[0,7],依次插入则最终成为一棵可用的线段树;时间复杂度为O(NlogK)
     
  • 最后是查询给定的M个点在N条线段出现的次数;如给定数字2,则递归到线段树中的节点[2,2],并将经过的节点的count值累加起来,最终就是数字2在给定的线段集合内出现的次数;

样例:

 /**
* count记录这条线段出现的次数
* */
struct Interval {
int left;
int right;
int count;
}; /**
* 此函数用于创建线段树的基本结构,InterTree是一个元素为Interval类型的指针数组
* 其大小为2*(max-min+1),也就是满二叉树;
* */
void ConstructIT(int min, int max, Interval** InterTree, int index) {
/**
* 构建当前节点
* index用于索引InterTree中当前元素的位置
* 遵循父节点为i,则左右子节点分别为2*i, 2*i+1
* */
Interval *temp=new Interval();
temp->count=;
temp->left=min;
temp->right=max;
InterTree[index]=temp;
/**
* 如果min和max相等,则说明已经到叶子节点
* */
if(min==max)
return;
/**
* 分别递归创建左右子节点
* */
int middle=(min+max)/;
ConstructIT(min,middle,InterTree,index*);
ConstructIT(middle+,max,InterTree,index*+);
}
/**
* 此函数用于注入线段树的线段标记信息
* */
void InsertInfor(int min, int max, Interval** InterTree, int index) {
/**
* 构建线段树的时候,middle本身属于left子树
* */
int middle=(InterTree[index]->left + InterTree[index]->right)/; if(max<=middle) {
/**
* 插入线段完全在middle左边
* */
InsertInfor(min, max, InterTree, *index);
} else if(min>middle) {
/**
* 插入线段完全在middle右边
* */
InsertInfor(min, max, InterTree, *index+);
} else if(min==InterTree[index]->left &&
max==InterTree[index]->right) {
/**
* 插入线段恰好是节点本身的端点范围,有可能是叶子节点
* */
InterTree[index]->count+=;
return;
} else if(InterTree[index]->left==InterTree[index]->right) {
/**
* 已经到达叶子节点,但是min和max仍旧不等,错误返回
* */
return;
} else {
/**
* middle将待插入的线段分成两段
* */
InsertInfor(min, middle, InterTree, *index);
InsertInfor(middle+, max, InterTree, *index+);
}
}
/**
* 此函数用于检查覆盖区间,注意此时的min和max可能超出
* InterTree的范围
* */
int CheckInterval(int min, int max, Interval** InterTree, int index) {
int middle=(InterTree[index]->left + InterTree[index]->right)/;
if(max<middle) {
return CheckInterval(min, max, InterTree, *index);
} else if(min>middle) {
return CheckInterval(min, max, InterTree, *index+);
} else if(min==InterTree[index]->left &&
max==InterTree[index]->right) {
return InterTree[index]->count;
} else {
return CheckInterval(min, middle, InterTree, *index)+
CheckInterval(middle+, max, InterTree, *index+);
}
} void Crossover(int *first, int lfirst, int *second, int lsecond) {
/**
* 获取first和second数组中各自的最大值与最小值的差值
* 选取差值较小的一个范围作为线段树的构建区间
* 节省空间消耗
* */
int minf=, maxf=,mins=,maxs=;
for(int i=;i<lfirst;i+=) {
if(maxf<first[i])
maxf=first[i];
if(minf>first[i])
minf=first[i];
}
for(int i=;i<lsecond;i+=) {
if(maxs<second[i])
maxs=second[i];
if(mins>second[i])
mins=second[i];
}
int min=minf,max=maxf;
int isFirst=true;
if((max-min)>(maxs-mins)) {
min=mins;
max=maxs;
isFirst=false;
}
/**
* 构建一个数组,数组元素为Interval*类型,
* 数组大小为线段树节点个数,由于线段树是
* 完全二叉树,所以节点数肯定为2N-1,N为区间
* 大小,也为叶节点数
* */
Interval *InterTree[*(max-min+)-];
/**
* 首先构建线段树的基本结构
* */
ConstructIT(min, max, InterTree, );
/**
* 然后将线段树范围大小对应的区间数组元素
* 插入到线段树中,如果区间在线段树之外则
* 需要特殊处理
* */
int *temp=NULL, length=;
int overlapSize=;
if(isFirst) {
for(int i=;i<lfirst;i+=) {
if(first[i]<InterTree[]->left ||
first[i-]>InterTree[]->right)
continue;
else if(first[i]==InterTree[]->left ||
first[i-]==InterTree[]->right)
overlapSize++;
else
InsertInfor(first[i-],first[i], InterTree, );
}
temp=second;
length=lsecond;
} else {
for(int i=;i<lsecond;i+=) {
if(second[i]<InterTree[]->left ||
second[i-]>InterTree[]->right)
continue;
else if(second[i]==InterTree[]->left ||
second[i-]==InterTree[]->right)
overlapSize++;
else
InsertInfor(second[i-],second[i], InterTree, );
}
temp=first;
length=lfirst;
}
/**
* 顺序将temp中的区间元素插入到InterTree中
* */ for(int i=;i<length;i+=) {
length+=CheckInterval(temp[i-],temp[i],InterTree,);
}
}

参考连接:
http://hi.baidu.com/alpc62/blog/item/469edeca0043e382c8176875.html

笔试算法题(42):线段树(区间树,Interval Tree)的更多相关文章

  1. 笔试算法题(31):将有序数组转换成BST表示 & 线段树的应用

    出题:要求将一个有序整数数组转换成最小深度的Binary Search Tree表示: 分析:由于需要是最小深度,所以BST应保持平衡,左右节点数大致相当,并且BST中当前根节点大于所有其左子树中的元 ...

  2. 笔试算法题(47):简介 - B树 & B+树 & B*树

    B树(B-Tree) 1970年由R. Bayer和E. Mccreight提出的一种适用于外查找的树,一种由BST推广到多叉查找的平衡查找树,由于磁盘的操作速度远小于存储器的读写速度,所以要求在尽量 ...

  3. 笔试算法题(40):后缀数组 & 后缀树(Suffix Array & Suffix Tree)

    议题:后缀数组(Suffix Array) 分析: 后缀树和后缀数组都是处理字符串的有效工具,前者较为常见,但后者更容易编程实现,空间耗用更少:后缀数组可用于解决最长公共子串问题,多模式匹配问题,最长 ...

  4. 前端如何应对笔试算法题?(用node编程)

    用nodeJs写算法题 咱们前端使用算法的地方不多,但是为了校招笔试,不得不针对算法题去练习呀! 好不容易下定决心 攻克算法题.发现js并不能像c语言一样自建输入输出流.只能回去学习c语言了吗?其实不 ...

  5. 笔试算法题(45):简介 - AC自动机(Aho-Corasick Automation)

    议题:AC自动机(Aho-Corasick Automation) 分析: 此算法在1975年产生于贝尔实验室,是著名的多模式匹配算法之一:一个常见的例子就是给定N个单词,给定包含M个字符的文章,要求 ...

  6. 笔试算法题(03):最小第K个数 & 判定BST后序序列

    出题:输入N个整数,要求输出其中最小的K个数: 分析: 快速排序和最小堆都可以解决最小(大)K个数的问题(时间复杂度为O(NlogN)):另外可以建立大小为K的最大堆,将前K个数不断插入最大堆,对于之 ...

  7. LeetCode算法题-Convert Sorted Array to Binary Search Tree(Java实现)

    这是悦乐书的第166次更新,第168篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第25题(顺位题号是108).给定一个数组,其中元素按升序排序,将其转换为高度平衡的二叉 ...

  8. LeetCode算法题-Second Minimum Node In a Binary Tree(Java实现)

    这是悦乐书的第285次更新,第302篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第153题(顺位题号是671).给定非空的特殊二叉树,其由具有非负值的节点组成,其中该树 ...

  9. 笔试算法题(58):二分查找树性能分析(Binary Search Tree Performance Analysis)

    议题:二分查找树性能分析(Binary Search Tree Performance Analysis) 分析: 二叉搜索树(Binary Search Tree,BST)是一颗典型的二叉树,同时任 ...

随机推荐

  1. CodeForces E. Goods transportation【最大流+dp最小割】

    妙啊 首先暴力建图跑最大流非常简单,s向每个i连流量为p[i]的边,每个i向t连流量为s[i]的边,每个i向j连流量为c的边(i<j),但是会又T又M 考虑最大流=最小割 然后dp求最小割,设f ...

  2. python网络爬虫之二requests模块

    requests http请求库 requests是基于python内置的urllib3来编写的,它比urllib更加方便,特别是在添加headers, post请求,以及cookies的设置上,处理 ...

  3. [poj3744] Scout YYF I【概率dp 数学期望】

    传送门:http://poj.org/problem?id=3744 令f(i)表示到i,安全的概率.则f(i) = f(i - 1) * p + f(i - 2) * (1 - p),若i位置有地雷 ...

  4. 51Nod 1315 合法整数集

    1315 合法整数集 题目来源: TopCoder 基准时间限制:1 秒 空间限制:131072 KB 分值: 10 难度:2级算法题 收藏 关注 一个整数集合S是合法的,指S的任意子集subS有Fu ...

  5. QT5每日一学(二)编写QT多窗口程序

    一.添加主窗口 1.首先打开Qt Creator,新建Qt Widgets Application,项目名称设置为windows,在类信息界面保持基类为QMainWindow.类名为MainWindo ...

  6. Tree CodeForces -932D

    错误记录:如下注释语句 #include<cstdio> #include<algorithm> using namespace std; typedef long long ...

  7. 贪心/数学 Codeforces Round #212 (Div. 2) A. Two Semiknights Meet

    题目传送门 /* 贪心/数学:还以为是BFS,其实x1 + 4 * k = x2, y1 + 4 * l = y2 */ #include <cstdio> #include <al ...

  8. 配置Ubuntu16.04第01步:U盘安装 Ubuntu 16.04系统

    Ubuntu 每年发布两个版本,Ubuntu 16.04 开发代号为“Xenial Xerus”,为第六个长期支持(LTS)版本. 1. 制作U盘系统安装盘 1.1下载最新的Universal USB ...

  9. github下载下来的C#控制台小游戏[含源码]

    早就听说了github是世界最大的源码库,但自己却不是很懂,今天去研究了下,注册了一个帐号,然后在上面搜索了一下C# game,然后发现有许多的游戏. 随意地选择了一个,感觉比较简单,于是就下载了下来 ...

  10. 用.NetReactor保护您的源码[转][修改]

    原文链接 前言 VS开发的源代码安全性,是很多开发者头痛的事情.于是保护好源代码便成了开发者们最关心的事情之一了. 在网上搜一搜,很多有不少的第三方工具可以为源代码加密.加密方式不外乎就是混淆,加壳. ...