:更正一下:在英文原版书中。“请给出一个能在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

确定一组矩形是否有两个重叠的算法相关推荐

  1. 【Vulkan学习记录-基础篇-2】用Vulkan画两个重叠的矩形

    在前一篇中完成了对Vulkan的初始化和三角形的绘制,其中很多东西还没有被用到,这一节最终将绘制这样两个重叠的矩形,并且它们会一直绕着屏幕中心点进行旋转. 将要补充使用的内容有:VertexBuffe ...

  2. 两个矩形相交的面积c语言,关于算法:两个旋转矩形的相交面积

    我有两个2D矩形,分别定义为原点(x,y),大小(高度,宽度)和旋转角度(0-360°). 我可以保证两个矩形的大小相同. 我需要计算这两个矩形的相交区域. 尽管可以,但计算不一定要精确. 我将结果与 ...

  3. html中怎么给长方形填充颜色,PS怎么在一个矩形里填充两种颜色 ps给矩形填充两种颜色的教程...

    PS怎么在一个矩形里填充两种颜色?看到别人设计的作品中,一个矩形被填充了两种不同颜色,这是如何操作的呢?ps小白们或许还不太清楚吧,今天,小编为大家带来了ps给矩形填充两种颜色的教程.感兴趣的朋友快来 ...

  4. html5如何让多张图片重叠,css怎么让两张图片重叠?

    css怎么让两张图片重叠?下面本篇文章给大家介绍一下使用CSS让两张图片重叠的方法.有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助. css怎么让两张图片重叠? 想要使用css把两个图 ...

  5. java 读取栅格,提取两个重叠栅格的数据

    我正在尝试提取两个重叠栅格集的数据(一个,一堆35个栅格,全部来自同一个源,第二个是高程栅格),以获取每个栅格的值(值的平均值)的data.frame所有栅格的像素 . 栅格堆栈的描述如下: > ...

  6. 解决图像目标检测两框重叠问题

    文章目录 1 问题现象 2 解决办法 3 Non-Maximum Suppression 原理 3.1 什么是非极大值抑制 3.2 为什么要用非极大值抑制 3.3 如何使用非极大值抑制 3.4 效果 ...

  7. mask rcnn算法分析_实例分割综述(单阶段/两阶段/实时分割算法汇总)

    作者:Danny明泽 来源:公众号@3D视觉工坊 链接:实例分割综述(单阶段/两阶段/实时分割算法汇总) 简介 目标检测或定位是数字图像从粗到细的一个渐进过程.它不仅提供了图像对象的类,还提供了已分类 ...

  8. 二维前缀和(矩形割补法)——杨子曰算法

    二维前缀和(矩形割补法)--杨子曰算法 今天曰一个很实用的技巧,二维前缀和 来,先问一个问题,如果我给出一个矩阵,让你求出任意一个子矩阵的和,你会怎么搞? 哦,简单简单,O(n^2)咔咔地暴力一下,欧 ...

  9. DayDayUp:本博主预计2019下半年将会出两本书(关于人工智能算法及其实战案例应用方向、计算机算法竞赛集锦方向),如有合作意向,请留言告知

    DayDayUp:本博主预计2019下半年将会出两本书(关于人工智能算法及其实战案例应用方向.计算机算法竞赛集锦方向),如有合作意向,请留言告知 PS:禁止任何形式的知识盗取!! 目录 关于人工智能算 ...

最新文章

  1. 通过分析 JDK 源代码研究 TreeMap 红黑树算法实现--转
  2. JAVA数据库编程(JDBC技术)-入门笔记
  3. window7开放端sqlserver端口
  4. Java NIO 介绍和基本demo
  5. opencv图像的基础操作
  6. 网站后端_Python+Flask.0007.FLASK构造跳转之301跳转与302重定向?
  7. WIN10中DOCKER的安装
  8. Spring Web Services 3.0.4.RELEASE和2.4.3.RELEASE发布
  9. 双亲委派模型【理解】
  10. html 火焰文字效果 霓虹文字效果(text-shadow)
  11. 市场上十块钱买一个随身WiFi使用技术手段,插卡流量不限速不限量?
  12. JAVA基础--IO流(字节流)
  13. (Linux)mongodb启用安全认证登录
  14. 总结:linux运维常用命令
  15. [附源码]SSM计算机毕业设计高校奖学金评定管理系统JAVA
  16. 不自量力是个恶毒的形容词,但这种心态是最不应该被嘲笑的
  17. 计算机考研要考科目,2022考研:计算机专业需要准备哪些科目?
  18. 【Linux】alias及设置
  19. 859-细谈安全的 HTTPS 协议
  20. 正常人白手起家挣一千万需要多久?

热门文章

  1. 各纬度气候分布图_读中国年平均气温分布图,寻找中国全年平均气温最高和最低的地方...
  2. 2个网页跳来跳去_怎么写最优化的网页标题标签(Title Tag)?
  3. 中国定制家具行业消费规模与发展形势分析报告2022版
  4. Shopee上线全球类目树,类目垂直定位,让商家引流更精准!
  5. python地图 两点距离_使用Python调用百度地图Api获取两地距离
  6. 国际农化巨头战略转移-丰收节贸易会:未来农化发展之路
  7. js比较查看两个数组之间是否相等
  8. 基本HTTP协议流程是什么?
  9. Android(java)学习笔记158:多线程断点下载的原理(JavaSE实现)
  10. DevExpress 里实现单选按钮