[置顶]图论算法大集锦(持续更新中)

1.图的存储:

  (1)邻接矩阵:

      
#include<iostream>
#include<cstdio>
#include<cstdlib>
#define MAXN 0x7fffffff//没有路径(边的长度为极限); 
#define maxn 9999//数据边的最大值; 
#define MAX 0x7fffffff/3//两极限边相加可能超过int 大小; 
using namespace std;


int m,n;//点的个数和边的个数;
int s[maxn][maxn];

void adjacency_matrix_storage()
{
    cin>>m>>n;
    for(int i=1;i<=n;++i)
        for(int j=1;j<=n;++j)
            s[i][j]=MAXN;
    for(int i=1;i<=n;++i)
    {
        int s1,s2,weight;
        cin>>s1>>s2>>weight;
        s[s1][s2]=weight;
        //s[s2][s1]=weight; 如果是无向边就加上这句; 
    }
    
} 

int main()
{
    adjacency_matrix_storage();
    //后面是其他操作; 
    return 0; 
}
邻接矩阵存储

  (2)邻接表:

      
#include<iostream>
#include<cstdio>
#include<cstdlib>
#define maxn 99999
#define MAXN 0x7fffffff/3
using namespace std;

struct node
{
    int a,b,w,next;
};
node edge[maxn];//用邻接表存储边; 

int head[maxn],sum=1;//存储顶点,sum为下标; 
int m,n; //顶点个数和边的个数; 

void push(int a,int b,int c)
{
    edge[sum].a=a;
    edge[sum].b=b;
    edge[sum].w=c;
    edge[sum].next=head[a];
    head[a]=sum++;
}

void Initialization()
{
    sum=1;
    for(int i=1;i<=m*n;++i)
    {    
        head[i]=-1;//用-1作为标志表明链表的结束; 
        edge[i].w=MAXN;
    }
}

int main()
{
    int i,j;
    cin>>m>>n;
    Initialization(); 
    for(i=1;i<=n;++i)
    {
        int a,b,c;
        cin>>a>>b>>c;
        push(a,b,c);//有向边; 
    }
    cin>>j;
    cout<<endl; 
    for(int k=head[j];k!=-1;k=edge[k].next)//调用顶点J相连的每一条边;
    {
        int l=edge[k].b;//L和顶点J的关系是:L和J之间存在一条从J指向L的有向线段; 
        cout<<j<<' '<<l<<endl;
    } 
    
    //······其他操作;
    return 0;
}
邻接表存储

一般情况下使用邻接表存储会降低使用空间大小和有效节约时间;但如果是比较稠密的图这两种方式就区别不大了;

2.图的遍历:

  (1)DFS:

      
#include<iostream>
#include<cstdio>
#include<cstdlib>
#define maxn 99999
#define MAXN 0x7fffffff/3
using namespace std;

struct node
{
    int a,b,w,next;
};
node edge[maxn];//用邻接表存储边; 

int head[maxn],sum=1;//存储顶点,sum为下标; 
int m,n; //顶点个数和边的个数;
bool visit[maxn]={false}; 

void push(int a,int b,int c)
{
    edge[sum].a=a;
    edge[sum].b=b;
    edge[sum].w=c;
    edge[sum].next=head[a];
    head[a]=sum++;
}

void Initialization()
{
    sum=1;
    for(int i=1;i<=m*n*2;++i)
    {    
        head[i]=-1;//用-1作为标志表明链表的结束; 
        edge[i].w=MAXN;
        visit[i]=false;
    }
}

void DFS(int sum)
{
    visit[sum]=true;
    if(edge[sum].b!=0)
    cout<<"-->"<<edge[sum].b;
    for(int k=head[sum];k!=-1;k=edge[k].next)//调用顶点J相连的每一条边;
        if(!visit[edge[k].b])
            DFS(edge[k].b);
}

int main()
{
    int i,j;
    cin>>m>>n;
    Initialization(); 
    for(i=1;i<=n;++i)
    {
        int a,b,c;
        cin>>a>>b>>c;
        push(a,b,c);//有向边; 
    }
    cin>>j;
    cout<<endl;
    cout<<j; 
    DFS(j);
    //······其他操作;
    return 0;
}
DFS邻接表

  (2)BFS:

      
#include<iostream>
#include<queue>
#include<cstdio>
#include<cstdlib>
#define maxn 99999
#define MAXN 0x7fffffff/3
using namespace std;

struct node
{
    int a,b,w,next;
};
node edge[maxn];//用邻接表存储边; 

int head[maxn],sum=1;//存储顶点,sum为下标; 
int m,n; //顶点个数和边的个数;
bool visit[maxn]={false},vis[maxn]={false}; 

void push(int a,int b,int c)
{
    edge[sum].a=a;
    edge[sum].b=b;
    edge[sum].w=c;
    edge[sum].next=head[a];
    head[a]=sum++;
}

void Initialization()
{
    sum=1;
    for(int i=1;i<=m*n*2;++i)
    {    
        head[i]=-1;//用-1作为标志表明链表的结束; 
        edge[i].w=MAXN;
        visit[i]=vis[i]=false;
    }
}

void DFS(int sum)
{
    visit[sum]=true;
    if(edge[sum].b!=0)
    cout<<"-->"<<edge[sum].b;
    for(int k=head[sum];k!=-1;k=edge[k].next)//调用顶点J相连的每一条边;
        if(!visit[edge[k].b])
            DFS(edge[k].b);
}

void BFS(int sum)
{
    queue<int>que;
    que.push(sum);
    vis[sum]=true;    
    cout<<sum;
    while(!que.empty())
    {
        int su=que.front();
        que.pop();
    
        for(int k=head[su];k!=-1;k=edge[k].next)//调用顶点J相连的每一条边;
        if(!vis[edge[k].b])
        {
            que.push(edge[k].b);
            vis[edge[k].b]=true;
            cout<<"-->"<<edge[k].b;
        }
    }
}

int main()
{
    int i,j;
    cin>>m>>n;
    Initialization(); 
    for(i=1;i<=n;++i)
    {
        int a,b,c;
        cin>>a>>b>>c;
        push(a,b,c);//有向边; 
    }
    cin>>j;
    cout<<endl;
    cout<<j; 
    DFS(j);
    cout<<endl<<"******************"<<endl;
    BFS(j);
    //······其他操作;
    return 0;
}
BFS邻接表

一般来说这两者都有不小的缺点:DFS深度优先搜索会调用大量的栈空间,很容易爆栈;

而BFS内存耗费量大(需要开大量的数组单元用来存储状态)

总而言之,这两者都在时空上有很大的局限性;慎用!

3.求最短路径

  (1)Flyoed:

      
#include <cstdio>
#include <iostream>
#define MAXN 99999999
using namespace std;

int dis[10][10],k,i,j,n,m;

int main() 
{
    
    cin >> n >> m;

    for(i=1; i<=n; i++)
        for(j=1; j<=n; j++)
            if(i==j) dis[i][j]=0;
            else dis[i][j]=MAXN;
    for(i=1; i<=m; i++) 
    {
        int a,b,c;
        cin >> a >> b >> c ;
        dis[a][b]=c;
    }
    for(k=1; k<=n; k++)
        for(i=1; i<=n; i++)
            for(j=1; j<=n; j++)
                if(dis[i][j]>dis[i][k]+dis[k][j] )
                    dis[i][j]=dis[i][k]+dis[k][j];
    int start,end;
    cin >> start >> end ;
    cout << dis[start][end] <<endl;
    return 0;
}
Flyoed

  (2)Dijkstra:

      
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
const int MAXN = 1010;
const int INF = 999999999;
int n, k;
int map[MAXN][MAXN];
bool visit[MAXN];
int pre[MAXN];
int dist[MAXN];


void init()
{
    memset(visit, false, sizeof(visit));
    for (int i = 1; i <= n; ++i){
        for (int j = 1; j <= n; ++j)
            map[i][j] = INF;
        pre[i] = i;
        dist[i] = INF;
    }
}
void Dijkstra(const int v)
{
    int i, j;
    int minValue, minNode;


    dist[1] = 0;
    visit[1] = true;

    for (i = 2; i <= n; ++i){
        if (map[1][i] != INF){
            dist[i] = map[1][i];
            pre[i] = 1;
        }
    }
    
    for (i = 1; i <= n; ++i)
    {
        minValue = INF;
        minNode = 0;

        for (j = 1; j <= n; ++j){
            if (!visit[j] && minValue > dist[j]){
                minNode = j;
                minValue = dist[j];
            }
        }
        if (minNode == 0)
            break;

        visit[minNode] = true;
        for (j = 1; j <= n; ++j)
        {
            if (!visit[j] && map[minNode][j] != INF && dist[j] > dist[minNode] + map[minNode][j])
            {
                dist[j] = dist[minNode] + map[minNode][j];
                pre[j] = minNode;
            }
        }

        if (minNode == v)
            break;
    }
}
Dijkstra

  (3)SPFA:

      
long long SPFA(int st)  
{  
    for(int i=1;i<=n;i++)  
        sp[i]=inf;  
    sp[1]=0;  
    queue<int> q;  
    q.push(st);  
    while(!q.empty())  
    {  
        int kai=q.front();q.pop();  
        for(int i=H[kai];i!=-1;i=E[i].next)  
        {  
            if(sp[E[i].v]>E[i].count+sp[kai]){  
                sp[E[i].v]=E[i].count+sp[kai];  
                q.push(E[i].v);  
            }  
        }  
    }  
    long long ans=0;  
    for(int i=1;i<=n;i++)  
        ans+=sp[i];  
    return ans;  
} 

void spfa(int s,int dis[])  
{  
    int i,pre[N];  
    bool used[N];  
    queue<int> q;  
    memset(used,0,sizeof(used));  
    memset(pre,-1,sizeof(pre));  
    for(i=0; i<N; i++)  
        dis[i]=inf;  
    dis[s]=0;  
    used[s]=true;  
    q.push(s);  
    while(!q.empty())  
    {  
        int u=q.front();  
        q.pop();  
        used[u]=false;  
        for(i=0; i<map[u].size(); i++)  
        {  
            Node p=map[u][i];  
            if(dis[p.v]>dis[u]+p.len)  
            {  
                dis[p.v]=dis[u]+p.len;  
                pre[p.v]=u;  
                if(!used[p.v])  
                {  
                    used[p.v]=true;  
                    q.push(p.v);  
                }  
            }  
        }  
    }  
}  
SPFA

三者的时间复杂度依次为:O(n^3),O(n^2),O(KE),其中E是边数,K是常数,平均值为2,图越稠密,K越大;

其中F·····和D·······算法比较的稳定,可是很慢啊,SPFA一般情况下比较快,可是一但图很稠密,就退化到N^2的复杂度;

但还是SPFA用的最多,也最好用,建议做最短路径的题用SPFA;

4.强联通分量

  (1)Kosaraju

      
#include<iostream>
#include<cstring>
#define maxn 1005
using namespace std;

bool s[maxn][maxn],s1[maxn][maxn];
bool visit[maxn];
int post[maxn];
int m,n,num=0,po=0;

void DFS(int a)
{
    visit[a]=true;
    for(int i=1;i<=n;++i)
        if(!visit[i]&&s[a][i])
            DFS(i);
    post[++po]=a;
}

void Dfs(int a)
{
    visit[a]=false;
    for(int i=1;i<=n;++i)
        if(visit[i]&&s1[a][i])
            Dfs(i);
}

int main()
{
    cin>>m>>n;
    for(int i=1;i<=n;++i)
    {
        int x,y;
        cin>>x>>y;
        s[x][y]=s1[x][y]=true;    
    }
    for(int i=1;i<=n;++i)
        if(!visit[i])
            DFS(i);
    for(int i=n;i>=1;--i)
        if(visit[post[i]])
        {
            Dfs(post[i]);
            num++;
        }
    cout<<num;
    return 0;    
}
kosaraju算法邻接矩阵

 

  (2)tarjan算法

      
#include<cstdio>
#include<iostream>
#include<cstring>
#define maxn 1005

using namespace std;

struct node
{
    int a,b,next;
};
node edge[maxn*maxn];

int DFN[maxn],low[maxn];
int s[maxn],head[maxn];
bool visit[maxn];

int sum=1,num=0,tail=0;

void push(int a,int b)
{
    edge[sum].a=a;
    edge[sum].b=b;
    edge[sum].next=head[a];
    head[a]=sum++;
}

void tarjan(int x)
{
    DFN[x]=low[x]=++num;
    s[++tail]=x;
    visit[x]=true;
    for(int i=head[x];i!=-1;i=edge[i].next)
    {
        if(!DFN[edge[i].b])
        {
            tarjan(edge[i].b);
            low[x]=min(low[x],low[edge[i].b]);
        }
        else if(visit[edge[i].b])
            low[x]=min(low[x],DFN[edge[i].b]);
    }
    if(low[x]==DFN[x])
    {
        do
        {
            printf("%d ",s[tail]);
            visit[s[tail--]]=false;
        }while (x!=s[tail+1]);
        printf("\n");
    }
    return;
}
int main()
{
    memset(head,-1,sizeof(head));
    int m,n;
    cin>>n>>m;
    for(int i=1;i<=m;++i)
    {
        int x,y;
        scanf("%d %d",&x,&y);
        push(x,y);
    }
    for(int i=1;i<=n;++i)
        if(!DFN[i])
            tarjan(i);
    return 0;
}
tarjan算法

 

Kosaraju算法有很大的优化的空间,例如可以改变存储方式、对搜索进行剪枝;

而tarjan算法比较好用,建议做强联通分量的题用tarjan算法;

5.最小生成树

  (1)Prim算法

      
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#define maxn 1005
using namespace std;

struct node 
{
    int a,b,w,next;
}
node s[maxn*maxn];
int head[maxn];
bool visit[maxn];
int sum=1,n,m,sss;

void push(int a,int b,int c)
{
    s[sum].a=a;
    s[sum].b=b;
    s[sum].w=c;
    s[sum].next=head[a];
    head[a]=sum++;
}
int sss=0;
void Prim(int i)
{
    int ss=1;
    sizeof(visit,false,sizeof(visit));
    visit[i]=true;
    while(ss!=m)
    {
        int ssss=99999,sdf=0;
        for(int k=head[i];k!=-1;k=s[k].next)
            if(!visit[s[k].b]&&s[k].w<sss)
            {
                ssss=s[k].w;
                sdf=s[k].b;
            }
        sss+=s[sdf].w;
        ++ss;
    }
}

int main()
{
    cin>>m>>n;
    for(int i=1;i<=n;++i)
    {
        int x,y,z;
        cin>>x>>y>>z;
        push(x,y,z);
    }
    Prim(1);
    cout<<sss;
}
Prim算法

 

  (2)Kruskal算法

      
#include<cstdio>  
#include<cstring>  
#include<cstdlib>  
#include<cmath>  
#include<iostream>  
#include<algorithm>  
#define maxn 510  
#define MAXN 0x3f3f3f3f  
  
using namespace std;

struct node
{
    int a,b,cost,next;
    bool operator < (const node& b) const
    {
        if(!cost)return false;
        else return cost<b.cost;
    }
}; 
node s[maxn*maxn];
int fa[maxn],m,n,q,sum=1;

int push(int a,int b,int c)
{
    s[sum].a=a;
    s[sum].b=b;
    s[sum].cost=c;
    sum++;
}

int find(int son)
{
    if(fa[son]!=son)
        fa[son]=find(fa[son]);
    return fa[son];
}

void unio(int a,int b)
{
    int f1=find(a);
    int f2=find(b);
    if(f1!=f2)
        fa[f2]=f1;
}
int sssss=0,sss=0;
void Kruskal()
{
    sort(s+1,s+sum+1);
    for(int i=1;i<=sum;++i)
    {
        if(find(s[i].a)!=find(s[i].b))
        {
            sssss+=s[i].cost;
            unio(s[i].a,s[i].b);
            sss++;
        }
        if(sss==n)
        return;
    }
}

int main()
{
    freopen("water.in","r",stdin);
    freopen("water.out","w",stdout);
    for(int i=0;i<=500;++i)
        fa[i]=i; 
    cin>>n;
    for(int i=1;i<=n;++i)
        for(int j=1;j<=n;++j)
        {
            int cost;
            cin>>cost;
            if(cost!=0)
            push(i,j,cost);
        }
    Kruskal();
    cout<<sssss;
}
Kruskal算法

 

Kruskal比较快,而Prim比较慢

 6.拓扑排序

  

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值