建图,链式前向星,拓扑排序
- 建图的三种方式
- 1.邻接矩阵
- 2.邻接表
- 3.链式前向星
- 拓扑排序
- 练习题-课程表
- 字典序最小的拓扑排序
- 拓扑排序模板
- 题目描述
- 输入格式
- 输出格式
- 样例 #1
- 样例输入 #1
- 样例输出 #1
- 提示
- 例题
- 1.火星字典
- 2.戳印序列
建图的三种方式
1.邻接矩阵
邻接矩阵,a[i,j]==true,代表i ——>j
2.邻接表
vector<vector< int >>arr
(1)有向图
- 无权图:vector<vector< int >>arr
- 有权图:vector<vector< pair< int,int > >>arr
(2)无向图
3.链式前向星
head[i]=j:表示节点i此时的第一条边为j号边;
next[i]=j:一个链表,表示i号边后面连着j号边;若j为 0,表示到尾巴了
to[i]=j:表示i号边去往j节点;
weight[i]=j:表示i号边的权重为j;
cnt:代表以及申请了几条边;
拓扑排序
定义入度:一个节点被指向的边数
进行下面过程:找出所有入度为0的点,将他们排在前面,然后删去他们以及他们的影响,重复上述行为。若发现过程中找不到入度为0的点,那么该图无法拓扑排序(也说明存在有向环);
练习题-课程表
有向边代表依赖关系
class Solution {
public:
int q[2003];
vector<int> findOrder(int n, vector<vector<int>>& prerequisites) {
vector<int>ans;
vector<int>kong;
int cnt=0;
vector<int>indegree(n,0);
vector<vector<int>> g(n);
for(auto p:prerequisites){
indegree[p[0]]++;
g[p[1]].push_back(p[0]);
}
int l=0,r=0;
for(int i=0;i<n;i++){
if(indegree[i]==0){
q[r++]=i;
}
}
while(l<r){
int now=q[l++];
cnt++;
ans.push_back(now);
for(int next:g[now]){
if((--indegree[next])==0){
q[r++]=next;
}
}
}
return cnt==n ? (ans): (kong);
}
};
字典序最小的拓扑排序
拓扑排序模板
题目描述
有向无环图上有n个点,m条边。求这张图字典序最小的拓扑排序的结果。字典序最小指希望排好序的结果中,比较靠前的数字尽可能小。
输入格式
第一行是用空格隔开的两个整数n和m,表示n个点和m条边。
接下来是m行,每行用空格隔开的两个数u和v,表示有一条从u到v的边。
输出格式
输出一行,拓扑排序的结果,数字之间用空格隔开
样例 #1
样例输入 #1
5 3
1 2
2 4
4 3
样例输出 #1
1 2 4 3 5
提示
1 ≤ n , m ≤ 1 0 5 1 \leq n,m \leq 10^5 1≤n,m≤105
注意:图上可能有重边
把队列换成优先队列就行了
#include<bits/stdc++.h>
using namespace std;
#define N 10002
priority_queue<int,vector<int>,greater<int>> q;
int ans[100002];
int cnt=0;
int main(){
int n,m;
cin>>n>>m;
vector<vector<int>> g(n+1);
vector<int> ig(n+1,0);
for(int i=0;i<m;i++){
int x,y;
cin>>x>>y;
ig[y]++;
g[x].push_back(y);
}
for(int i=1;i<=n;i++){
if(ig[i]==0){
q.push(i);
}
}
while(!q.empty()){
int now=q.top();
ans[cnt++]=now;
q.pop();
for(int next:g[now]){
if((--ig[next])==0){
q.push(next);
}
}
}
for(int i=0;i<cnt;i++){
cout<<ans[i]<<' ';
}
return 0;
}
例题
1.火星字典
https://leetcode.cn/problems/Jf1JuT/description/
有向边代表大小关系
class Solution {
public:
int ig[30];
int q[100];
int cnt=0;
int real=0;
string alienOrder(vector<string>& words) {
string ans;
fill(ig,ig+30,-1);
vector< vector<int>> g(30);
for(string x:words){
for(char tmp:x){
if(ig[tmp-'a']==-1){
ig[tmp-'a']=0;
real++;
}
}
}
for(int i=0;i<(words.size()-1);i++){
string x=words[i],y=words[i+1];
int j;
for( j=0;(j<x.length())&&(j<y.length());j++){
if(x[j]!=y[j]){
ig[y[j]-'a']++;
g[x[j]-'a'].push_back(y[j]-'a');
break;
}
}
if(j<x.length() && j==y.length())return "";
}
int l=0,r=0;
for(int i=0;i<26;i++){
if(ig[i]==0){
q[r++]=i;
}
}
while(l<r){
int now=q[l++];
ans+=(now+'a');
cnt++;
for(int next:g[now]){
if((--ig[next])==0){
q[r++]=next;
}
}
}
return cnt==real ? ans : "";
}
};
2.戳印序列
https://leetcode.cn/problems/stamping-the-sequence/
有点难度,很难想到用拓扑排序
我们发现以下性质,1、如果最终能够得到target,那么最后一步的印章一定是都对得上的
2.不会重复印同一个位置,所以无需找最优解,我们只需判断每个位置都印一遍是否可行,所以最终判断的是数组ans大小是否等于n-m+1
一种隐蔽的拓扑排序,即前一次盖章对后一次盖章有依赖关系
class Solution {
public:
int q[1003];
bool flag[1003];//保证target每个位置都最多查询一遍
vector<int> kong;
vector<int> movesToStamp(string stamp, string target) {
int n=target.length(),m=stamp.length();
vector<int> ans;
vector<int> ig(n-m+1,0);
vector<vector<int>> g(n+1);
for(int i=0;i<n-m+1;i++){
for(int j=0;j<m;j++){
if(stamp[j]!=target[i+j]){
ig[i]++;
g[i+j].push_back(i);//g[x]表示与target中x下标位置不相同的印章位置
}
}
}
int l=0,r=0;
for(int i=0;i<n-m+1;i++){
if(ig[i]==0){
q[r++]=i;
}
}
while(l<r){
int now=q[l++];
ans.push_back(now);
for(int k=now;k<now+m && k<n;k++){
if(!flag[k]){
flag[k]=1;
for(int j:g[k]){
if((--ig[j])==0){
q[r++]=j;
}
}
}
}
}
if(ans.size()!=n-m+1)return kong;
reverse(ans.begin(),ans.end());
return ans;
}
};