题目来自编程之美
题目
思路(1):
预处理:O(n)
把给出的所有区间全部映射到一个一维数组上,该一维数组中的元素等于元素下标所在区间的起始位置
查询:O(1)
首先判断待查区间的终点是否在一维数组上,
如果在,看终点的起点是否小于待查区间的起点,如果小于等于,则包含。如果大于,则不包含。
如果不在,则已知区间不包含待查区间。
举例:
已知区间:1 3;2 5;7 9;
待查区间:1 4;2 8
经过预处理后,一维数组的值:
-1表示:6和0没有出现在已知的区间中
5对应的1表示:5所在区间的起始位置为1.
缺点:该方法限制比较多
(1)区间必须为非负
(2)区间的端点不能太大,否则导致数组会很大
(3)不能支持区间动态添加或者删除。
添加区间时,如果待添加区间的端点大于数组的终点,则要扩大数组长度,并且数组重新赋值,消耗大。
删除区间时,需要重新设置发生改变的区间的起始端点。
代码
#include <iostream>
#include <assert.h>
using namespace std;
struct Interval
{
int m_nSrart;
int m_nEnd;
};
int* InitInterval(Interval ArrItl[20],int nCount,int& nMaxEnd)
{
for (int i = 0;i < nCount;i++)
{
assert(ArrItl[i].m_nEnd >= ArrItl[i].m_nSrart);
nMaxEnd = max(nMaxEnd,ArrItl[i].m_nEnd);
}
assert(nMaxEnd > 0);
int* pArrDstItl = new int[nMaxEnd + 1];
memset(pArrDstItl,-1,(nMaxEnd + 1) * sizeof(int));//-1表示区间不包括该值
//把被包含的点的值赋值为0
for (int i = 0;i < nCount;i++)
{
for (int nCur = ArrItl[i].m_nSrart;nCur <= ArrItl[i].m_nEnd;nCur++)
{
pArrDstItl[nCur] = 0;
}
}
//设置每一个被包含的点的起点
int nStart = -1;
for (int i = 0;i <= nMaxEnd;i++)
{
if (pArrDstItl[i] != -1)
{
if (nStart == -1)
{
nStart = i;
}
pArrDstItl[i] = nStart;
}
else
{
nStart = -1;
}
}
return pArrDstItl;
}
bool IsCover(Interval ArrItl[20],int nCount,Interval srcItl)
{
assert(srcItl.m_nSrart <= srcItl.m_nEnd);
assert(srcItl.m_nSrart > -1);
int nMaxEnd = -0x3f3f3f3f;
int* pArrDstItl = InitInterval(ArrItl,nCount,nMaxEnd);
assert(pArrDstItl);
if (nMaxEnd >= srcItl.m_nEnd)
{
if (pArrDstItl[srcItl.m_nEnd] != -1 && pArrDstItl[srcItl.m_nEnd] <= srcItl.m_nSrart)
{
return true;
}
else
{
return false;
}
}
else
{
return false;
}
}
int main()
{
int nCount = 0;
Interval ArrItl[20];
cin>>nCount;
assert(nCount < 20);
for (int i = 0;i < nCount;i++)
{
cin>>ArrItl[i].m_nSrart;
cin>>ArrItl[i].m_nEnd;
}
cout<<"输入待查询的区间:"<<endl;
Interval srcItl;
cin>>srcItl.m_nSrart;
cin>>srcItl.m_nEnd;
if (IsCover(ArrItl,nCount,srcItl))
{
cout<<"包含!"<<endl;
}
else
{
cout<<"不包含!"<<endl;
}
system("pause");
return 1;
}
/*
3
2 3
1 2
3 9
输入待查询的区间:
1 6
包含!
*/
思路(2)编程之美的思路
离线操作:根据区间的起始值,对给出的区间排序,之后区间合并。时间复杂度O(n)
在线操作:根据区间的起始值,进行二分查找。时间复杂度O(nlogn)。
缺点:不能支持区间动态添加或者删除。数组存储时,添加和删除操作不方便。
代码
#include <iostream>
#include <algorithm>
#include <assert.h>
using namespace std;
struct Interval
{
int m_nSrart;
int m_nEnd;
bool operator< (Interval Itl)
{
return m_nSrart < Itl.m_nSrart;
}
};
int BinSearch(Interval ArrNewItl[20],int nNewCount,int nValue)
{
assert(nNewCount > 0);
int nLow = 0;
int nHigh = nNewCount - 1;
int nMid = 0;
while(nLow <= nHigh)
{
nMid = (nHigh + nLow) >> 1;
if (ArrNewItl[nMid].m_nSrart > nValue)
{
nHigh = nMid - 1;
}
else if (ArrNewItl[nMid].m_nSrart < nValue)
{
nLow = nMid + 1;
}
else
{
return nMid;
}
}
return nHigh;
}
void InitInterval(Interval ArrItl[20],int nCount,Interval ArrNewItl[20],int& nNewCount)
{
assert(nCount > 0 && nNewCount == 0);
sort(ArrItl,ArrItl + nCount);
//合并区间
int nCur = 0;
int nStart = -1;
while(nCur < nCount)
{
//寻找相交区间
while(nCur < nCount - 1 && ArrItl[nCur].m_nEnd >= ArrItl[nCur + 1].m_nSrart)
{
if (nStart == -1)
{
nStart = nCur;
}
++nCur;
}
//把此区间插入新数组中
if (nStart != -1) //下标区间[nStart,nCur]对应的区间都是连续的,可以合并
{
ArrNewItl[nNewCount].m_nSrart = ArrItl[nStart].m_nSrart;
ArrNewItl[nNewCount].m_nEnd = ArrItl[nCur].m_nEnd;
}
else //下标nCur对应的区间是一个独立的,直接插入数组。
{
ArrNewItl[nNewCount].m_nSrart = ArrItl[nCur].m_nSrart;
ArrNewItl[nNewCount].m_nEnd = ArrItl[nCur].m_nEnd;
}
nStart = -1;
nCur++;
nNewCount++;
}
}
bool IsCover(Interval ArrItl[20],int nCount,Interval srcItl)
{
assert(srcItl.m_nSrart <= srcItl.m_nEnd);
assert(srcItl.m_nSrart > -1);
Interval ArrNewItl[20];
int nNewCount = 0;
//初始化区间(合并区间)
InitInterval(ArrItl,nCount,ArrNewItl,nNewCount);
//查找区间
int nPos = BinSearch(ArrNewItl,nNewCount,srcItl.m_nSrart);
if (nPos < 0)
{
return false;
}
else
{
if (ArrNewItl[nPos].m_nEnd >= srcItl.m_nEnd)
{
return true;
}
else
{
return false;
}
}
}
int main()
{
int nCount = 0;
Interval ArrItl[20];
cin>>nCount;
assert(nCount < 20);
for (int i = 0;i < nCount;i++)
{
cin>>ArrItl[i].m_nSrart;
cin>>ArrItl[i].m_nEnd;
}
cout<<"输入待查询的区间:"<<endl;
Interval srcItl;
cin>>srcItl.m_nSrart;
cin>>srcItl.m_nEnd;
if (IsCover(ArrItl,nCount,srcItl))
{
cout<<"包含!"<<endl;
}
else
{
cout<<"不包含!"<<endl;
}
system("pause");
return 1;
}
/*
3
1 3
2 5
7 8
输入待查询的区间:
1 4
1 8
0 1
*/
/*
4
1 3
2 5
7 8
7 10
输入待查询的区间:
1 4
1 8
0 1
7 9
*/
/*
4
1 3
2 5
7 8
10 10
输入待查询的区间:
1 4
1 8
0 1
7 9
*/
思路三、线段树