编程之美2.19—区间重合判断

来源:互联网 发布:黑马手机安全卫士源码 编辑:程序博客网 时间:2024/05/21 17:32

题目:

给定一个源区间[x,y](y>=x)和N个无序的目标区间[x1,y1] [x2,y2] [x3,y3]…[xn,yn],判断源区间[x,y]是不是在目标区间内?

例如,给定源区间[1,6]和一组无序的目标区间[2,3] [1,1] [3,9],即可认为区间[1,6]在区间[2,3] [1,1] [3,9]内,因为目标区间实际上是[1,9]。


基本思想:

解法一

问题的本质在于对目标区间的处理。一个比较直接的思路即将源区间[x,y](y>=x)和N个无序的目标区间[x1,y1] [x2,y2] [x3,y3]…[xn,yn]逐个投影到坐标轴上,只考察源区间未被覆盖的部分。如果所有的目标区间全部投影完毕,仍然有源区间没有被覆盖,那么源区间就不在目标区间之内。

仍以[1,6]和 [2,3] [1,2] [3,9]为例,考察[1,6]是否在[2,3] [1,2] [3,9]内:

源区间为[1,6],那么最初未被覆盖的部分为{[1,6]},将按顺序考察目标区间[2,3] [1,2] [3,9] 。

将目标区间[2,3]投影到坐标轴,那么未被覆盖的部分{[1,6]}都变为{[1,2],[3,6]}。

将目标区间[1,2]投影到坐标轴,那么未被覆盖的部分{[1,2],[3,6]}将变为{ [3,6]}。

将目标区间[3,9]投影到坐标轴,那么未被覆盖的部分{ [3,6]}将变为{o},即可说明[1,6]是在[2,3] [1,2] [3,9]内。

由以上步骤可以看出,每次操作,尚未被覆盖的区间数组大小最多增加1(当然可能减少),而每投影一个新的目标区间,计算有哪些源区间数组被覆盖需要O(logN)的时间复杂度,但是更新尚未被覆盖的区间数组需要O(N)的时间复杂度,所以总的时间复杂度为O(N^2)。

这个解法的复杂度高,而且如果要对k组源区间进行查询,那么时间复杂度会增大单次查询的k倍。有没有更好的解法,使得单次查询的时间复杂度降低,并且使k次查询的时间复杂度小于单次查询的时间复杂度的1/k倍呢? 

解法二

一种值得尝试并已经在本书中多次运用的思路是,对现有的数组进行一些预处理(如合并、排序等),将无序的目标区间合并成几个有序的区间,这样就可以进行区间的比较。

因此,问题就变成了如何将这些无序的数组转化为一个目标区间。

首先可以做一次数据初始化的工作。由于目标区间数组是无序的,因此可以对其进行合并操作,使其变得有序。即先将目标区间数组按X轴坐标从小到大排序(排序时可采用快速排序等),如[2,3] [1,2] [3,9]-> [1,2] [2,3] [3,9];接着扫描排序后的目标区间数组,将这些区间合并成若干个互不相交的区间,如[1,2] [2,3] [3,9]-> [1,9]。

然后在数据初始化的基础上,运用二分查找(为什么?)来判定源区间[x,y]是否被合并后的这些互补相交的区间中的某一个包含。如[1,6]被[1,9]包含,则可说明[1,6]在[2,3] [1,2] [3,9]内。

这种思路相对简单,时间复杂度计算如下:

排序的时间复杂度:O(N*logN)(N为目标区间的个数);

合并的时间复杂度:O(N);

单次查找的时间复杂度:logN;

所以总的时间复杂度为O(N*logN)+ O(N)+ O(k*logN)= O(N*logN) + O(k*logN),k为查询的次数,合并目标区间数组的初始化数据操作只需要进行一次。

这样不仅单次查询的时间复杂度降低了,而且对于k>>N的情况,处理起来也会方便很多。

总结

解法一采用利用目标区间来分割源区间的方法,会增加存储空间;解法二采用合并的方法,既简单又节省了空间。

解法二源代码:

#include <iostream>  using namespace std;void swap(int a,int b){int t=a;a=b;b=t;}void quicksort(int low,int high,int *a,int *b)  { int i,j,temp1,temp2;if(low>high)return;temp1=a[low];temp2=b[low];i=low;j=high;while(i!=j){while(a[j]>=temp1&&i<j) j--;while(a[i]<=temp1&&i<j) i++;if(i<j){swap(a[i],a[j]);swap(b[i],b[j]);}}a[low]=a[i];b[low]=b[i];a[i]=temp1;b[i]=temp2;quicksort(low,i-1,a,b);quicksort(i+1,high,a,b);}      void unite(int *a,int *b,int *c,int *d,int length,int& number){  int temp=0; c[temp]=a[0];      for(int i=1;i<=length-1;i++) {  if(b[i-1]>=a[i])continue;d[temp++]=b[i-1];c[temp]=a[i]; }  d[temp]=b[length-1]; number=temp+1; cout<<"合并后的区间为c[i]<->d[i]:"<<endl;for(i=0;i<=temp;i++) //这里的c[i]与d[i]构成合成后的区间  cout<<c[i]<<" "<<d[i]<<endl; cout<<endl; }  bool LookUp(int *c,int *d,int begin,int end,int x,int y) //二分查找求满足条件的区间{  int middle; if(begin == end && begin == 0) { //若最后合并只得到一个数组  if(c[0]<=x && d[0]>=y)return true;else return false;}  while(begin<=end)  {  middle=(begin+end)/2; if(c[middle]<x)begin=middle+1; else  end=middle-1;  }  cout<<c[end]<<" "<<c[begin]<<endl; if(c[end]<=x&&d[end]>=y)return true;  return false;  }  bool projection(int *a,int *b,int n,int x,int y)  {  quicksort(0,n-1,a,b);cout<<"排序后的区间为a[i]<->b[i]:"<<endl;for(int i=0;i<n;i++) //输出排序后的a,b数组cout<<a[i]<<" "<<b[i]<<endl;cout<<endl;  int *c=new int [n];      int *d=new int [n];      int length;      unite(a,b,c,d,n, length);      return LookUp(c,d,0,length-1, x, y);      delete[]c;      delete[]d; } int main() { //两组测试数据int flag;cin>>flag;//0第一份数据,1第二份数据if(!flag){int n=4;  int *a=new int [n];//对应 x1, x2, ... , xn  int *b=new int [n];//对应 y1, y2, ... ,yn  a[0]=1;  a[1]=5;  a[2]=2;  a[3]=10;  b[0]=3;  b[1]=9;  b[2]=4;  b[3]=11;  int x=6;  int y=10;  if(projection(a,b,n,x,y))//1结果在目标区间内,0不在cout<<"in."<<endl;elsecout<<"not in."<<endl; delete []a;  delete []b; }else{     int n=3; int *a=new int [n]; int *b=new int [n]; a[0]=2; a[1]=1; a[2]=3; b[0]=3; b[1]=2; b[2]=9;  int x=1; int y=6; if(projection(a,b,n,x,y))//1结果在目标区间内,0不在cout<<"in."<<endl;elsecout<<"not in."<<endl; delete []a;  delete []b; }    return 0;  }  

 


引用:

http://blog.csdn.net/tianshuai1111/article/details/7828961

http://www.cnblogs.com/aiyelinglong/archive/2012/04/28/2475569.html






0 0
原创粉丝点击