848. 有向图的拓扑序列
给定一个 n 个点 m 条边的有向图,点的编号是 1 到 n,图中可能存在重边和自环。
请输出任意一个该有向图的拓扑序列,如果拓扑序列不存在,则输出 −1。
若一个由图中所有点构成的序列 A 满足:对于图中的每条边 (x,y),x 在 A 中都出现在 y 之前,则称 A 是该图的一个拓扑序列。
输入格式
第一行包含两个整数 n 和 m。
接下来 m 行,每行包含两个整数 x 和 y,表示存在一条从点 x 到点 y 的有向边 (x,y)。
输出格式
共一行,如果存在拓扑序列,则输出任意一个合法的拓扑序列即可。
否则输出 −1。
数据范围
1≤n,m≤10^5
输入样例:
3 3
1 2
2 3
1 3
输出样例:
1 2 3
如果只要是个拓扑序列就行的话:
是个容器就能存 >< 喜欢啥就用啥 反正进容器的都是入度为0的,正着取倒着取都无所谓
如果要求输出按字典序排列的拓扑排序序列,那选择有序的容器就行了:
一是优先队列 二是set
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
int n,e;
int in[maxn]={0};
vector<int> res;
vector<int> g[maxn];
bool topSort(){
//queue<int> q;
priority_queue<int,vector<int>,greater<int>> q;
for(int i=1;i<=n;i++){
if(in[i]==0){
q.push(i);
}
}
while(!q.empty()){
int top=q.top();
res.push_back(top);
q.pop();
for(int i=0;i<g[top].size();i++){
int k=g[top][i];
in[k]--;
if(in[k]==0) q.push(k);
}
if(res.size()==n) return true;
}
return false;
}
int main(){
cin>>n>>e;
for(int i=0;i<e;i++){
int a,b;
scanf("%d%d",&a,&b);
g[a].push_back(b);
in[b]++;
}
bool flag=topSort();
if(!flag) cout<<"-1"<<endl;
else{
for(int i:res){
printf("%d ",i);
}
}
}
也可以用set
其实对set我一直了解甚少。。。
直到某个题npy带我的时候才知道set是有序的hhhh。所以在这里可以代替优先队列啦。 当然这个速度会慢一点
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
int n,e;
int in[maxn]={0};
vector<int> res;
vector<int> g[maxn];
set<int> s;
bool topSort(){
//queue<int> q;
for(int i=1;i<=n;i++){
if(in[i]==0){
s.insert(i);
}
}
while(!s.empty()){
int top=*s.begin();
res.push_back(top);
s.erase(s.begin());
for(int i=0;i<g[top].size();i++){
int k=g[top][i];
in[k]--;
if(in[k]==0) s.insert(k);
}
if(res.size()==n) return true;
}
return false;
}
int main(){
cin>>n>>e;
for(int i=0;i<e;i++){
int a,b;
scanf("%d%d",&a,&b);
g[a].push_back(b);
in[b]++;
}
bool flag=topSort();
if(!flag) cout<<"-1"<<endl;
else{
for(int i:res){
printf("%d ",i);
}
}
}
STL set的妙处
至于set,还有一些性质…
set的insert和delete是不会影响旁边元素的iterator 然后insert可以指定位置insert(可以复杂度O1 如果位置是对的
位置是错的就自动调用普通insert
- 底层红黑树 节点方式存储,所以插入删除效率高 时间复杂度就按照RB树分析吧
- iterator这里就相当于指向节点的指针,内存没有变,指向内存的指针怎么会失效呢(当然被删除的那个元素本身已经失效了)。相对于vector来说,每一次删除和插入,指针都有可能失效,调用push_back在尾部插入也是如此。 而vector插入删除后指针可能会变,当发现内存不够时,要把以前的内存释放,申请更大的内存。
- 看set的增删改查方法,能够发现这个真的很强。。。。
删除操作
既能删除一个,又能删除一段
-
erase(iterator) ,删除定位器iterator指向的值
-
erase(first,second),删除定位器first和second之间的值
比如erase(s.begin(),s.end()) -
erase(key_value),删除键值key_value的值
查找操作
- find() 返回定位器 没找到返回end() ,s.end()
插入操作
既能插入一个 又能插入一段
int a[] = {1,2,3};
set<int> s;
s.insert(a,a+3);
for(set<int>::iterator iter = s.begin() ; iter != s.end() ; ++iter)
{
cout<<*iter<<" ";
}
//返回值类型是void
set<int>::iterator=s.insert(5);
//如果原本就有了,那就返回原来的位置
//返回值类型pair<set<int>::iterator,bool>
其他操作
-
lower_bound(key_value) ,返回第一个大于等于key_value的定位器
-
upper_bound(key_value),返回最后一个大于等于key_value的定位器