zoukankan      html  css  js  c++  java
  • 数据结构&图论:图

    在这里对图的存储和遍历进行一个规范,为以后更复杂的数据结构学习打下基础

    首先是邻接矩阵的形式,适合于存稠密图,如果是全连接图就再合适不过了

    int a[maxn][maxn];

    一个二维数组就可以搞定了,如果是bool值那么就是不带权值的

    a[i][j]=w表示i->j这条边的权值为w,反之亦然

    建图操作是很显然的

        for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            cin>>a[i][j];

    接下来是一个DFS:

    int vis[maxn];
    void dfs(int dp,int x)
    {
        cout<<x<<" ";
        for(int i=1;i<=n;i++)
        if(a[x][i]&&!vis[i])
        {
            vis[i]=1;
            dfs(dp+1,i);
        }
    }

    特别强调一下,我是先输出的结果,再进行的遍历,如果给定的图不是连通的,给了一片森林的话,一定要注意相应的细节

    在这里的细节处理是给刚开始进入dfs的点打上标记,不要漏掉这里

    然后是BFS,处理方法类似

    int q[maxn];
    void bfs(int x)
    {
        int h=0,t=1;
        q[t]=x;
        while(h!=t)
        {
            h=h%maxn+1;
            cout<<q[h]<<" ";
            for(int i=1;i<=n;i++)
            if(a[q[h]][i]&&!vis[i])
            {
                vis[i]=1;
                t=t%maxn+1;
                q[t]=i;
            }
        }
    }

    接下来给出一个完整的例子:

     1 #include<iostream>
     2 #include<cstring>
     3 using namespace std;
     4 const int maxn=1005;
     5 int n;
     6 int a[maxn][maxn];
     7 int vis[maxn];
     8 void dfs(int dp,int x)
     9 {
    10     cout<<x<<" ";
    11     for(int i=1;i<=n;i++)
    12     if(a[x][i]&&!vis[i])
    13     {
    14         vis[i]=1;
    15         dfs(dp+1,i);
    16     }
    17 }
    18 int q[maxn];
    19 void bfs(int x)
    20 {
    21     int h=0,t=1;
    22     q[t]=x;
    23     while(h!=t)
    24     {
    25         h=h%maxn+1;
    26         cout<<q[h]<<" ";
    27         for(int i=1;i<=n;i++)
    28         if(a[q[h]][i]&&!vis[i])
    29         {
    30             vis[i]=1;
    31             t=t%maxn+1;
    32             q[t]=i;
    33         }
    34     }
    35 }
    36 int main()
    37 {
    38     cin>>n;
    39     for(int i=1;i<=n;i++)
    40     for(int j=1;j<=n;j++)
    41         cin>>a[i][j];
    42     vis[1]=1;
    43     dfs(1,1);
    44     memset(vis,0,sizeof(vis));
    45     cout<<endl;
    46     vis[1]=1;
    47     bfs(1);
    48     return 0;
    49 }
    50 /*
    51 0 1 1 0 0
    52 1 0 0 1 1
    53 1 0 0 0 0
    54 0 1 0 0 0
    55 0 1 0 0 0
    56 */

    然后就是邻接链表了,邻接链表的存储形式是用的最多的

    struct Edge
    {
        int t,w,next;
    }e[maxm];
    int g[maxn];
    int tot=0;

    dalao们喜欢用vector改成邻接数组,我不是dalao,所以就先这样了(*^▽

    意义还是很明显的,t代表着这条边的to节点,如果需要记录起始节点,就要存一个u了

    由于是链表,next是必不可少的,存的是与此边共起点的下一条边的编号

    然后就是g数组了,存的是由下标节点所引出的第一条边的编号

    建图采用的是链表的头插法

    void addedge(int a,int b,int c)
    {
        tot++;
        e[tot].t=b;
        e[tot].w=c;
        e[tot].next=g[a];
        g[a]=tot;
    }

    然后我们给出DFS和BFS的部分,这里原理是一模一样的,唯一需要注意的就是,数据结构变了,循环变量什么的要相应的调整

    int vis[maxn];
    void dfs(int dp,int x)
    {
        cout<<x<<" ";
        for(int tmp=g[x];tmp;tmp=e[tmp].next)
        if(!vis[e[tmp].t])
        {
            vis[e[tmp].t]=1;
            dfs(dp+1,e[tmp].t);
        }
    }
    int q[maxn];
    void bfs(int x)
    {
        int h=0,t=1;
        q[t]=x;
        while(h!=t)
        {
            h=h%maxn+1;
            cout<<q[h]<<" ";
            for(int tmp=g[q[h]];tmp;tmp=e[tmp].next)
            if(!vis[e[tmp].t])
            {
                vis[e[tmp].t]=1;
                t=t%maxn+1;
                q[t]=e[tmp].t;
            }
        }
    }

    这里再补充一下刚才提到的森林的问题,由于我们是进入的时候就直接输出,所以第一个点一定要提前做好标记

    由于是森林,每个点都要跑一边BFS或者DFS,那么在其他点的时候,进入之前判断vis,如果可行,打上vis之后再去跑,这样就没有任何问题了

    最后给出邻接链表的完整实现:

     1 #include<iostream>
     2 #include<cstring> 
     3 using namespace std;
     4 const int maxn=1005;
     5 const int maxm=1005;
     6 int n,m;
     7 struct Edge
     8 {
     9     int t,w,next;
    10 }e[maxm];
    11 int g[maxn];
    12 int tot=0;
    13 void addedge(int a,int b,int c)
    14 {
    15     tot++;
    16     e[tot].t=b;
    17     e[tot].w=c;
    18     e[tot].next=g[a];
    19     g[a]=tot;
    20 }
    21 int vis[maxn];
    22 void dfs(int dp,int x)
    23 {
    24     cout<<x<<" ";
    25     for(int tmp=g[x];tmp;tmp=e[tmp].next)
    26     if(!vis[e[tmp].t])
    27     {
    28         vis[e[tmp].t]=1;
    29         dfs(dp+1,e[tmp].t);
    30     }
    31 }
    32 int q[maxn];
    33 void bfs(int x)
    34 {
    35     int h=0,t=1;
    36     q[t]=x;
    37     while(h!=t)
    38     {
    39         h=h%maxn+1;
    40         cout<<q[h]<<" ";
    41         for(int tmp=g[q[h]];tmp;tmp=e[tmp].next)
    42         if(!vis[e[tmp].t])
    43         {
    44             vis[e[tmp].t]=1;
    45             t=t%maxn+1;
    46             q[t]=e[tmp].t;
    47         }
    48     }
    49 }
    50 int main()
    51 {
    52     cin>>n>>m;
    53     for(int i=1;i<=m;i++)
    54     {
    55         int x,y,z;
    56         cin>>x>>y>>z;
    57         addedge(x,y,z);
    58         addedge(y,x,z);
    59     }
    60     vis[1]=1;
    61     dfs(1,1);
    62     memset(vis,0,sizeof(vis));
    63     cout<<endl;
    64     vis[1]=1;
    65     bfs(1);
    66     return 0;
    67 } 

    邻接链表是我最喜欢的一种存图的形式了

    最后就是我觉得比较奇葩的邻接数组,是为了三级项目刻意实现的,当然如果把数组换成vector会更自然一些

    这里直接给出完整实现,和邻接链表大同小异,唯一的区别就是不用存next,直接枚举数组下标就可以了

     1 #include<iostream>
     2 #include<cstring> 
     3 using namespace std;
     4 const int maxn=1005;
     5 const int maxm=1005;
     6 int n,m;
     7 struct Edge
     8 {
     9     int t,w;
    10 };
    11 struct Graph
    12 {
    13     int cur;
    14     Edge e[maxm];
    15 }g[maxn];
    16 int tot=0;
    17 void addedge(int a,int b,int c)
    18 {
    19     tot++;
    20     Edge tmp;
    21     tmp.t=b;
    22     tmp.w=c;
    23     g[a].cur++;
    24     int t=g[a].cur;
    25     g[a].e[t]=tmp;
    26 }
    27 int vis[maxn];
    28 void dfs(int dp,int x)
    29 {
    30     cout<<x<<" ";
    31     for(int tmp=1;tmp<=g[x].cur;tmp++)
    32     if(!vis[g[x].e[tmp].t])
    33     {
    34         vis[g[x].e[tmp].t]=1;
    35         dfs(dp+1,g[x].e[tmp].t);
    36     }
    37 }
    38 int q[maxn];
    39 void bfs(int x)
    40 {
    41     int h=0,t=1;
    42     q[t]=x;
    43     while(h!=t)
    44     {
    45         h=h%maxn+1;
    46         cout<<q[h]<<" ";
    47         for(int tmp=1;tmp<=g[q[h]].cur;tmp++)
    48         if(!vis[g[q[h]].e[tmp].t])
    49         {
    50             vis[g[q[h]].e[tmp].t]=1;
    51             t=t%maxn+1;
    52             q[t]=g[q[h]].e[tmp].t;
    53         }
    54     }
    55 }
    56 int main()
    57 {
    58     cin>>n>>m;
    59     for(int i=1;i<=m;i++)
    60     {
    61         int x,y,z;
    62         cin>>x>>y>>z;
    63         addedge(x,y,z);
    64         addedge(y,x,z);
    65     }
    66     vis[1]=1;
    67     dfs(1,1);
    68     memset(vis,0,sizeof(vis));
    69     cout<<endl;
    70     vis[1]=1;
    71     bfs(1);
    72     return 0;
    73 } 

    在每一种形式中,DFS和BFS先入栈和先入队的点是可能不一样的,但是结果都是正确的,在用之前一定要现在纸上画一画,避免输出顺序错误

  • 相关阅读:
    集成学习
    逻辑斯谛回归
    【腾讯】【实习】【笔试】【数据分析师】2018.04.05
    C语言中scanf和printf的用法详解
    寻找一个数组中未出现的最小正整数(数组元素可重复)
    吃鸡问题中的的动态规划
    Coursera 深度学习 吴恩达 deep learning.ai 第三课 结构化机器学习项目 第二周 测试题目 Autonomous driving (case study)
    【转】P问题与NP问题详细解答加举例
    LeetCode 149. Max Points on a Line
    LeetCode 150. Evaluate Reverse Polish Notation
  • 原文地址:https://www.cnblogs.com/aininot260/p/9323914.html
Copyright © 2011-2022 走看看