题目来自编程之美
题目
思路(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 */思路三、线段树