建图,链式前向星,拓扑排序


建图的三种方式

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 1n,m105

注意:图上可能有重边

把队列换成优先队列就行了

#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;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值