确定一组矩形是否有两个重叠的算法
注:更正一下:在英文原版书中。“请给出一个能在O(nlgn)”时间里确定一组矩形中是否有两个重叠的算法。
”而不是中文版的 O(lgn).由于这个问题里涉及的排序算法就至少是O(nlgn)。
基本思想:
经提示用以矩形横坐标x为轴作为扫描线。从全部矩形x最小值到矩阵x最大值,当然在这之前要对全部矩形的横坐标x做一个排序,我用的是归并排序。
扫描过程如图所看到的的三个矩形中,从x1開始扫描。遇到矩形Gi的左x坐标,将Gi的纵坐标y的低端点和高端点组成的区间插入区间树后,推断矩阵Gi与区间树中已有的区间是否重叠,若重叠则返回真以证明重叠矩形存在,若没有重叠,则继续扫描x。
假设遇到矩形Gi的右x坐标,说明再往后扫描就不会与Gi矩形重叠,所以把Gi删除。如此循环往复。
图中所给三个矩形G1,G2,G3.应该从x1扫描到x2程序就自己主动终止证明重叠矩形存在,不会扫描到G3。
整体来看:①先做归并排序(时间O(nlgn)) ②再做区间树的插入删除以及重叠操作(O(nlgn)).
代码例如以下:
"MERGE_SORT.h"头文件
struct Array
{int key;int index;
};
void MERGE(struct Array B[],int p,int q,int r)
{int n1=q-p+1,n2=r-q,flag=-1,i,j;//不能为数组A里面的数。
struct Array *L=new struct Array[n1]; struct Array *R=new struct Array[n2]; for (i=1;i<=n1;i++) { L[i-1].key=B[p+i-1].key; L[i-1].index=B[p+i-1].index; } for (j=1;j<=n2;j++) { R[j-1].key=B[q+j].key; R[j-1].index=B[q+j].index; } L[n1].key=flag; R[n2].key=flag; i=0;j=0; for (int k=p;k<=r;k++) { if (L[i].key==flag) { B[k].key=R[j].key; B[k].index=R[j].index; j++; } else if (R[j].key==flag) { B[k].key=L[i].key; B[k].index=L[i].index; i++; } else if (L[i].key<=R[j].key) { B[k].key=L[i].key; B[k].index=L[i].index; i++; } else { B[k].key=R[j].key; B[k].index=R[j].index; j++; } } } void MERGE_SORT(struct Array B[],int p,int r) { if (p<r) { int q=(p+r)/2; MERGE_SORT(B,p,q); MERGE_SORT(B,q+1,r); MERGE(B,p,q,r); } }
主函数+区间树
#include <iostream>
#include <conio.h>
#include "MERGE_SORT.h"
using namespace std;
#define BLACK 0
#define RED 1
#define Nil -1
#define LEN sizeof(struct Tree)
#define n 4//矩形的个数
struct Tree*root=NULL;
struct Tree*nil=NULL;
struct interval
{int low,high;
};
struct Rectangular
{int flag;struct interval x,y;
};
struct Tree
{struct Tree*right,*left;struct Tree*parent;struct interval Int;int Max;int key;int color;
};
int MAX(int a,int b,int c)
{int temp=a>b?a:b;return temp>c?temp:c;
}
void LEFT_ROTATE(struct Tree*x)
{//左旋转:分三个步骤①②③来叙述旋转代码的。struct Tree*y=x->right;//设置y结点。
x->right=y->left;//本行代码以及以下的if结构表达的是“y的左孩子成为x的右孩子”。① if(y->left!=nil) { y->left->parent=x; } y->parent=x->parent;//本行代码以及以下的if-else结构表达的过程是“y成为该子树新的根”。② if(x->parent==nil) { root=y; } else if(x==x->parent->left) { x->parent->left=y; } else x->parent->right=y; y->left=x;//本行代码以及以下一行都表达了“x成为y的左孩子”。
③ x->parent=y; y->Max=x->Max; x->Max=MAX(x->Int.high,x->left->Max,x->right->Max); } void RIGHT_ROTATE(struct Tree*x) {//右旋转:分三个步骤①②③来叙述旋转代码的。
struct Tree*y=x->left;//设置y结点。
x->left=y->right;//本行代码以及以下的if结构表达的是“y的左孩子成为x的右孩子”。① if(y->right!=nil) { y->right->parent=x; } y->parent=x->parent;//本行代码以及以下的if-else结构表达的过程是“y成为该子树新的根”。② if(x->parent==nil) { root=y; } else if(x==x->parent->right) { x->parent->right=y; } else x->parent->left=y; y->right=x;//本行代码以及以下一行都表达了“x成为y的左孩子”。③ x->parent=y; y->Max=x->Max; x->Max=MAX(x->Int.high,x->left->Max,x->right->Max); } void RB_INSERT_FIXUP(struct Tree*z) { while (z->parent->color==RED) { if (z->parent==z->parent->parent->left) { struct Tree*y=z->parent->parent->right;//叔结点 if (y->color==RED)//情况一:叔结点为红色 {//给p1,y,p2着色以保持性质5。
而且攻克了z的父结点和z都是红色结点问题 z->parent->color=BLACK; y->color=BLACK; z->parent->parent->color=RED; z=z->parent->parent;//把z的祖父结点当成新结点z进入下一次循环 } else { if (z==z->parent->right)//情况二:检查z是否是一个右孩子且叔结点为黑色,前提是p1结点不是叶子结点 {//使用一个左旋让情况2转变为情况3 z=z->parent; LEFT_ROTATE(z);//因为进入if语句后可知旋转结点不可能是叶子结点。这样就不用推断z是否是叶子结点了。
} z->parent->color=BLACK;//情况三:是z是一个左孩子且叔结点为黑色,改变z的父和祖父结点颜色并做一次右旋,以保持性质5 z->parent->parent->color=RED; RIGHT_ROTATE(z->parent->parent);//因为p2可能是叶子结点。所以不妨用一个if推断 } } else//以下else分支相似于上面,注意到else分支的情况2和情况3所做旋转正好是if分支情况的逆。 { struct Tree*y=z->parent->parent->left; if (y->color==RED) { z->parent->color=BLACK; y->color=BLACK; z->parent->parent->color=RED; z=z->parent->parent; } else { if (z==z->parent->left) { z=z->parent; RIGHT_ROTATE(z); } z->parent->color=BLACK; z->parent->parent->color=RED; LEFT_ROTATE(z->parent->parent); } } } root->color=BLACK;//最后给根结点着为黑色。 } void RB_INSERT(struct Tree* z) { z->key=z->Int.low; struct Tree*y=nil; struct Tree*x=root; while (x!=nil) { y=x; x->Max=MAX(x->Int.high,x->Max,z->Int.high); if (z->key<x->key) { x=x->left; } else x=x->right; } z->parent=y; if (y==nil) { root=z; } else if(z->key<y->key) { y->left=z; } else y->right=z; z->left=nil;//给插入结点左右孩子赋值为空。 z->right=nil; z->color=RED;//给插入结点着为红色。 z->Max=z->Int.high;//+ RB_INSERT_FIXUP(z); } void RB_TRANSPLANT(struct Tree*u,struct Tree*v) { if (u->parent==nil) root=v; else if(u==u->parent->left) u->parent->left=v; else u->parent->right=v; v->parent=u->parent; } //非递归版本号的查找二叉查找树的最小值 struct Tree*ITERATIVE_TREE_MINIMUM(struct Tree*x) { while (x->left!=nil) { x=x->left; } return x; } //非递归版本号的二叉查找树查找函数 struct Tree*ITERATIVE_TREE_SEARCH(struct Tree*x,int k) { while (x!=nil&&k!=x->key) { if (k<x->key) { x=x->left; } else x=x->right; } return x; } void RB_DELETE_FIXUP(struct Tree*x) { struct Tree*w=NULL;//w是x的兄弟结点 while (x!=root&&x->color==BLACK)//假设x是黑色而且不是根结点。才进行循环。 {//x是一个具有双重颜色的结点,调整的目的是把x的黑色属性向上移动。
if (x==x->parent->left) { w=x->parent->right; if (w->color==RED)//情况一:x的兄弟结点w是红色的。 {//改变w和x.p的颜色+一次旋转使其变为情况二。三,四。
w->color=BLACK; x->parent->color=RED; LEFT_ROTATE(x->parent); w=x->parent->right; } if (w->left->color==BLACK&&w->right->color==BLACK)//情况二:x的兄弟结点w是黑色的。而且w的两个子节点都是黑色。 { w->color=RED;//从x和w上去掉一重黑色。
x还是黑色,而w变为红色。 x=x->parent;//x结点向上移动成为新的待调整结点。 } else { if (w->right->color==BLACK)//情况三:x的兄弟结点w是黑色的,w的左孩子是红色的。w的右孩子是黑色的。 {//交换w和w.left的颜色+旋转使其转变为情况四。
w->left->color=BLACK; w->color=RED; RIGHT_ROTATE(w); w=x->parent->right; } w->color=x->parent->color;//以下是情况四:x的兄弟结点w是黑色的,且w的右孩子是红色的。
x->parent->color=BLACK;//置x.p和w.right为黑色+旋转使其去掉x的额外黑色。
w->right->color=BLACK; LEFT_ROTATE(x->parent); x=root;//x成为根结点,结束循环。 } } else//以下和上面的if分支相似。不做累述。 { w=x->parent->left; if (w->color==RED) { w->color=BLACK; x->parent->color=RED; RIGHT_ROTATE(x->parent); w=x->parent->left; } if (w->left->color==BLACK&&w->right->color==BLACK) { w->color=RED; x=x->parent; } else { if (w->left->color==BLACK) { w->right->color=BLACK; w->color=RED; LEFT_ROTATE(w); w=x->parent->left; } w->color=x->parent->color; x->parent->color=BLACK; w->left->color=BLACK; RIGHT_ROTATE(x->parent); x=root; } } } x->color=BLACK; } void RB_DELETE(struct Tree*z) { struct Tree*y=z,*x;//y为待删除或待移动结点 int y_original_color=y->color;//保存y的原始颜色,为做最后的调整做准备。
if (z->left==nil) { x=z->right;//x指向y的唯一子结点或者是叶子结点。保存x的踪迹使其移动到y的原始位置上 RB_TRANSPLANT(z,z->right);//把以z.right为根的子树替换以z为根的子树。 } else if (z->right==nil) { x=z->left;//x指向y的唯一子结点或者是叶子结点,保存x的踪迹使其移动到y的原始位置上 RB_TRANSPLANT(z,z->left);//把以z.left为根的子树替换以z为根的子树。 } else { y=ITERATIVE_TREE_MINIMUM(z->right);//找到z.right的后继。 y_original_color=y->color;//y的新的原始结点被重置。 x=y->right;//x指向y的唯一子结点或者是叶子结点,保存x的踪迹使其移动到y的原始位置上 if (y->parent==z) { x->parent=y;//因为z的父结点是要删除的结点。所以不能指向它,于是指向y } else { RB_TRANSPLANT(y,y->right);//把以y.right为根的子树替换以y为根的子树。 y->right=z->right; y->right->parent=y; } RB_TRANSPLANT(z,y);//把以y为根的子树替换以z为根的子树。
y->left=z->left; y->left->parent=y; y->color=z->color;//把已经删除的结点颜色赋值给y,保证了y以上的树结构红黑性质不变。 } struct Tree*k=x->parent; while (k!=nil) { k->Max=MAX(k->left->Max,k->right->Max,k->Int.high); k=k->parent; } if(y_original_color==BLACK) //y的原始颜色为黑色。说明须要调整红黑颜色。 RB_DELETE_FIXUP(x); } bool overlap(struct interval x,struct interval i) { if (x.high<i.low||i.high<x.low) { return true;//没有重叠 } else { return false; } } struct Tree *INTERVAL_SEARCH(struct Tree *T,struct interval i) { struct Tree *x=T; while (x!=nil&&overlap(x->Int,i)) { if (x->left!=nil&&x->left->Max>=i.low) { x=x->left; } else x=x->right; } return x; } bool Rectangle_overlap(struct Rectangular A[],struct Array B[]) {//推断n个矩阵是否重叠,执行时间为O(nlgn) int i=1; while (i!=2*n) { if (A[B[i].index].flag==0)//0代表矩形Ri的纵坐标的还未进入扫描线。 { struct Tree*z=new struct Tree[LEN]; z->key=A[B[i].index].y.low; z->Int.low=A[B[i].index].y.low; z->Int.high=A[B[i].index].y.high; A[B[i].index].flag=1; if (root!=z&&INTERVAL_SEARCH(root,z->Int)!=nil) {//假设矩形重叠存在,那么直接返回。 return true; } RB_INSERT(z);//将这个矩形插入进区间树 } else//否则,矩形Ri的纵坐标进入过扫描线了,那么遇到的横坐标(B[i].key代表横坐标)必定是Ri的高端点。 { if (i==2*n-1||A[B[i+1].index].y.low!=A[B[i].index].y.high) { struct Tree*z=ITERATIVE_TREE_SEARCH(root,A[B[i].index].y.low);//先找到区间树中的这个结点。 RB_DELETE(z);//从区间树中删除这个矩形。
} } i++; } return false; } void init(struct Rectangular A[],struct Array B[]) {//区间树初始化。
nil=new struct Tree[LEN];//设置叶子结点 nil->key=Nil;nil->color=BLACK; root=nil; int i=0; struct Tree*z=new struct Tree[LEN];//设置根结点。 z->key=A[B[i].index].y.low; z->Int.low=A[B[i].index].y.low; z->Int.high=A[B[i].index].y.high; RB_INSERT(z); root=z; A[B[i].index].flag=1; } int char_int() { char ch; int i=0,s=0,t=1; char ch1[n]={0}; while (ch!=' ') { if (ch=='#') { while (i) { cout<<"\b"; ch1[i]=' '; i--; } } ch=getch(); cout<<ch; ch1[i++]=ch; } cout << "\b";//\b退格符号 while (i>1) { s+=(ch1[i---2]-'0')*t; t*=10; cout << "\b"; } return s; } void main() { struct Rectangular A[n]={0}; struct Array B[2*n]={0}; for (int i=0,j=0;i<n,j<2*n;i++,j+=2) { cout<<"请输入第"<<i<<"个矩阵的数据:(按#号键又一次输入,按空格键结束输入)"<<endl; cout<<"x的左端点=";cout<<(A[i].x.low=char_int()); cout<<" x的右端点=";cout<<(A[i].x.high=char_int()); cout<<" y的低端点=";cout<<(A[i].y.low=char_int()); cout<<" y的高端点=";cout<<(A[i].y.high=char_int())<<endl; B[j].key=A[i].x.low; B[j+1].key=A[i].x.high; B[j].index=i; B[j+1].index=i; } MERGE_SORT(B,0,2*n-1);//归并排序。时间为O(nlgn) init(A,B); if(Rectangle_overlap(A,B)) { cout<<"重叠矩阵存在!"<<endl; } else cout<<"重叠矩阵不存在"<<endl; }
最后測试时注意。和通常输入数据不太一样的是按空格结束输入而且按#号又一次输入!
例子输入:
转载于:https://www.cnblogs.com/gavanwanggw/p/6706231.html
确定一组矩形是否有两个重叠的算法相关推荐
- 【Vulkan学习记录-基础篇-2】用Vulkan画两个重叠的矩形
在前一篇中完成了对Vulkan的初始化和三角形的绘制,其中很多东西还没有被用到,这一节最终将绘制这样两个重叠的矩形,并且它们会一直绕着屏幕中心点进行旋转. 将要补充使用的内容有:VertexBuffe ...
- 两个矩形相交的面积c语言,关于算法:两个旋转矩形的相交面积
我有两个2D矩形,分别定义为原点(x,y),大小(高度,宽度)和旋转角度(0-360°). 我可以保证两个矩形的大小相同. 我需要计算这两个矩形的相交区域. 尽管可以,但计算不一定要精确. 我将结果与 ...
- html中怎么给长方形填充颜色,PS怎么在一个矩形里填充两种颜色 ps给矩形填充两种颜色的教程...
PS怎么在一个矩形里填充两种颜色?看到别人设计的作品中,一个矩形被填充了两种不同颜色,这是如何操作的呢?ps小白们或许还不太清楚吧,今天,小编为大家带来了ps给矩形填充两种颜色的教程.感兴趣的朋友快来 ...
- html5如何让多张图片重叠,css怎么让两张图片重叠?
css怎么让两张图片重叠?下面本篇文章给大家介绍一下使用CSS让两张图片重叠的方法.有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助. css怎么让两张图片重叠? 想要使用css把两个图 ...
- java 读取栅格,提取两个重叠栅格的数据
我正在尝试提取两个重叠栅格集的数据(一个,一堆35个栅格,全部来自同一个源,第二个是高程栅格),以获取每个栅格的值(值的平均值)的data.frame所有栅格的像素 . 栅格堆栈的描述如下: > ...
- 解决图像目标检测两框重叠问题
文章目录 1 问题现象 2 解决办法 3 Non-Maximum Suppression 原理 3.1 什么是非极大值抑制 3.2 为什么要用非极大值抑制 3.3 如何使用非极大值抑制 3.4 效果 ...
- mask rcnn算法分析_实例分割综述(单阶段/两阶段/实时分割算法汇总)
作者:Danny明泽 来源:公众号@3D视觉工坊 链接:实例分割综述(单阶段/两阶段/实时分割算法汇总) 简介 目标检测或定位是数字图像从粗到细的一个渐进过程.它不仅提供了图像对象的类,还提供了已分类 ...
- 二维前缀和(矩形割补法)——杨子曰算法
二维前缀和(矩形割补法)--杨子曰算法 今天曰一个很实用的技巧,二维前缀和 来,先问一个问题,如果我给出一个矩阵,让你求出任意一个子矩阵的和,你会怎么搞? 哦,简单简单,O(n^2)咔咔地暴力一下,欧 ...
- DayDayUp:本博主预计2019下半年将会出两本书(关于人工智能算法及其实战案例应用方向、计算机算法竞赛集锦方向),如有合作意向,请留言告知
DayDayUp:本博主预计2019下半年将会出两本书(关于人工智能算法及其实战案例应用方向.计算机算法竞赛集锦方向),如有合作意向,请留言告知 PS:禁止任何形式的知识盗取!! 目录 关于人工智能算 ...
最新文章
- 通过分析 JDK 源代码研究 TreeMap 红黑树算法实现--转
- JAVA数据库编程(JDBC技术)-入门笔记
- window7开放端sqlserver端口
- Java NIO 介绍和基本demo
- opencv图像的基础操作
- 网站后端_Python+Flask.0007.FLASK构造跳转之301跳转与302重定向?
- WIN10中DOCKER的安装
- Spring Web Services 3.0.4.RELEASE和2.4.3.RELEASE发布
- 双亲委派模型【理解】
- html 火焰文字效果 霓虹文字效果(text-shadow)
- 市场上十块钱买一个随身WiFi使用技术手段,插卡流量不限速不限量?
- JAVA基础--IO流(字节流)
- (Linux)mongodb启用安全认证登录
- 总结:linux运维常用命令
- [附源码]SSM计算机毕业设计高校奖学金评定管理系统JAVA
- 不自量力是个恶毒的形容词,但这种心态是最不应该被嘲笑的
- 计算机考研要考科目,2022考研:计算机专业需要准备哪些科目?
- 【Linux】alias及设置
- 859-细谈安全的 HTTPS 协议
- 正常人白手起家挣一千万需要多久?
热门文章
- 各纬度气候分布图_读中国年平均气温分布图,寻找中国全年平均气温最高和最低的地方...
- 2个网页跳来跳去_怎么写最优化的网页标题标签(Title Tag)?
- 中国定制家具行业消费规模与发展形势分析报告2022版
- Shopee上线全球类目树,类目垂直定位,让商家引流更精准!
- python地图 两点距离_使用Python调用百度地图Api获取两地距离
- 国际农化巨头战略转移-丰收节贸易会:未来农化发展之路
- js比较查看两个数组之间是否相等
- 基本HTTP协议流程是什么?
- Android(java)学习笔记158:多线程断点下载的原理(JavaSE实现)
- DevExpress 里实现单选按钮