数据结构Project 报告

版权声明:都是TATQAQ2333大爷教我的 https://blog.csdn.net/u012076197/article/details/86259025

数据结构Project 报告

毒液哥 Fudan University

问题描述与定义

维护一个二维平面上的数据结构,要求支持:

  1. 插入一个点
  2. 删除一个点
  3. 插入一个多边形
  4. 删除一个多边形
  5. 询问一个点在哪些多边形内部
  6. 询问一个多边形内部有哪些点

算法设计流程

朴素算法

若给定一个点和一个多边形,我们知道可以使用射线法判断该点是否在该多边形中。

那么很容易地,我们得到一个朴素的算法:用vector记录每个插入;用unordered_map记录每个元素是否删除;对于每个询问点,暴力查询插入过的未删除的多边形,用射线法判断其是否在该多边形中。

改进1-线段树优化

若一个点在一个边数为 n n 的多边形中,则它在x轴上的投影(以下用投影代替)一定在多边形的投影的内部。射线法的复杂度为 O ( n ) O(n) ,若能用不多于 O ( n ) O(n) 的时间排除一个投影不在多边形投影中的点,算法的时间性能可以提升。

使用线段树维护,将一个点或多边形根据其在的投影插入到线段树中。询问操作时先在线段树中查询到所有投影被包含的点,再分别射线法判断。

由于线段树只能维护整数坐标,考虑将原坐标放大 1 e 11 1e11 倍后取整。

改进2-将多边形拆分成若干条线段

若要判断一个点是否在一个边数为 n n 的凸多边形中,射线法的复杂度为 O ( n ) O(n) 。但一条射向y轴负方向的射线(以下用射线代替),只会经过该凸多边形的一条边,而射线法却会判断多边形所有的边。

若以一点为起点的射线,经过了多边形的某条边,则该点的投影一定在该边的投影内部。

改进上一个算法,将多边形拆分成若干条边(每条边要储存多边形编号),根据其所在的投影插入到线段树中。

每次查询一个多边形时,用一个unordered_map记录每个点穿过了该多边形几次,将该多边形每条边根据其投影在线段树上对点进行查询。

查询点同理,用一个unordered_map记录每个多边形被该点穿过了几次,将该点根据其投影在线段树上对每条线段进行查询。

改进3-对原坐标进行适当缩放

改进2算法在本机运行时间约1分钟,经过调试发现,插入操作占用约55秒。

考虑降低插入操作的用时。在改进2中,坐标的范围在经过缩放操作后达到2 ^ 60级别,这会导致线段树层数达到60层,且线段树节点必须用long long类型保存。为了缩小坐标范围,我们直接将原坐标取整。这会导致改进2中的性质——若以一点为起点的射线,经过了多边形的某条边,则该点的投影一定在该边的投影内部——退化。由“投影内部”退化为“投影内部及附近”。因此,插入部分的性能会提高,但查询部分的性能会降低。若想继续提高插入部分性能,可以将坐标继续缩小,反之若想提高查询部分性能,则要将坐标放大。

猜你喜欢

转载自blog.csdn.net/u012076197/article/details/86259025