开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第12天,点击查看活动详情
区间合并
给定 n 个区间 [l,r],要求合并所有有交集的区间。
注意如果在端点处相交,也算有交集。
输出合并完成后的区间个数。
1.模板介绍
首先按照人的思维,就是判断下一个区间的左端点是否小于上一个区间的右端点,如上 [1,3],[2,6] ,此时2<3并且2>1,所以左端点为1,两个可以合并;
并且判断此时右端点,3<6,所以右端点为大的6;
// 将所有存在交集的区间合并
//此时返回值为void,参数是地址传递,所以最后直接将结果赋值即可
void merge(vector<PII> &segs)
{
vector<PII> res;
//先根据左端点进行排序
sort(segs.begin(), segs.end());
//给出左右端点的初始值,最小值。与他们比较的值都大于他们
int st = -2e9, ed = -2e9;
for (auto seg : segs)
//判断当前右端点是否小于下一个区间的左端点,
//如果小于 说明两个区间无法合并
if (ed < seg.first)
{
if (st != -2e9) res.push_back({st, ed});
//重新赋值 当前比较的端点
st = seg.first, ed = seg.second;
}
//如果可以合并,那么判断最大的右端点
else ed = max(ed, seg.second);
//如果左端点不是初始值,那么赋值一个进去
if (st != -2e9) res.push_back({st, ed});
segs = res;
}
2.完整代码
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
const int MAX=1e5+10;
int main()
{
//创建二维数组存放区间
vector<pair<int,int>>A;
int n;
cin>>n;
for(int i=0;i<n;i++)
{
int l,r;
cin>>l>>r;
A.push_back({l,r});
}
//进行排序,默认是按左端点排序
sort(A.begin(),A.end());
int r=1;
//初始化最大右端点
int maxr=A[0].second;
for(int i=1;i<n;i++)
{
//如果下一个区间的左端点大于当前的最大右端点,那么这两个区间没有交集
if(A[i].first>maxr)
{
r++;
//重新赋值最大右端点
maxr=A[i].second;
}else
{
//否则说明有交集,那么判断当前的最大右端点是什么
maxr=max(maxr,A[i].second);
}
}
cout<<r;
return 0;
}
单链表(加餐)
1.模板介绍
class Node{
public:
int val;
Node* next;
};
静态单链表
// head存储链表头,e[]存储节点的值,ne[]存储节点的next指针,idx表示当前用到了哪个节点
int head, e[N], ne[N], idx;
// 初始化
void init()
{
head = -1;
idx = 0;
}
// 在链表头插入一个数a
void insert(int a)
{
e[idx] = a, ne[idx] = head, head = idx ++ ;
}
// 将头结点删除,需要保证头结点存在
void remove()
{
head = ne[head];
}
问题:为什么不用结构体构建链表
Node* node = new Node();,中间有一个 new 关键字来为新对象分配空间。
new的底层涉及内存分配,调用构造函数,指针转换等多种复杂且费时的操作。一秒大概能new1w次左右。
在平时的工程代码中,不会涉及上万次的new操作,所以这种结构是一种 见代码知意 的好结构。
但是在算法比赛中,经常碰到操作在10w级别的链表操作,如果使用结构体这种操作,是无法在算法规定时间完成的。
所以,在算法比赛这种有严格的时间要求的环境中,不能频繁使用new操作。也就不能使用结构体来实现数组。
#include<iostream>
using namespace std;
const int N=1e5+10;
//head 作为头指针
//e[N] 存储数据域
//ne[N] 存储指针域
//idx 表示已经使用到第几个节点了
int head,e[N],ne[N],idx;
//初始化 head=-1 表示空;idx=0 表示当前无节点
void init()
{
head=-1;
idx=0;
}
//头插
void add_to_head(int x)
{
//存入数据域
e[idx]=x;
//第一个添加的就成为了最后一个节点,ne[idx]=-1,指向空
//接下来ne[idx]=idx++,所以从头到尾的 ne[idx]是从高向低的,ne[idx]越高就越靠近头节点
ne[idx]=head;
head=idx++;
}
//指定位置插入
void add(int k,int x)
{
e[idx]=x;
//idx指的当前使用第几个节点了,
ne[idx]=ne[k];
//因为使用到的idx个节点插到了k位置之前,要知道idx是顺序的,0 1 2 3 ,所以k的指针域就是idx+1了
ne[k]=idx++;
}
//ne[k]指的是k指向的节点,所以ne[ne[k]]是k指向的下一个节点所指向的节点 eg: 1 2 3,1就是指向
void remove(int k)
{
ne[k]=ne[ne[k]];
}
int main()
{
init();
int n;
cin>>n;
while(n--)
{
char x;
int i,j;
cin>>x;
if(x=='H')
{
cin>>i;
add_to_head(i);
}else if(x=='I')
{
cin>>i>>j;
//下标从0开始的
add(i-1,j);
}else
{
cin>>i;
//如果是0 那么就删除头节点
if(i==0)
{
head=ne[head];
}else
{
remove(i-1);
}
}
}
for(int i=head;i!=-1;i=ne[i])
{
cout<<e[i]<<" ";
}
return 0;
}