• 图的邻接矩阵和邻接链表表示


     图的邻接矩阵表示:

      下面的这个程序读入一组定义一个无向图的便,创建一个对应这个图的邻接矩阵。如果在图中顶点i,j或j,i之间有一条边,就把a[i][j]和a[j][i]置为1,如果不存在这样的边,则置0。

    #include<iostream>
    using namespace std;
    int main()
    {
        int N;
        int i,j;
        cin>>N;
        int **adj=new int*[N];
        for( i=0;i<N;i++)
            adj[i]=new int[N];
        for(i=0;i<N;i++)
            for(j=0;j<N;j++)
                adj[i][j]=0;
        for(i=0;i<N;i++) adj[i][i]=1;
    
        while(cin>>i>>j)
        {
            adj[i][j]=1;
            adj[j][i]=1;
        }
    }

    另一种图的直观表示方法是链表数组,也叫邻接表(adjacent list),我们为每个顶点保存一个链表。

     这2中方法都是简单数据结构的数组-----都对每个顶点描述了和该顶点关联的边。对邻接矩阵,这个简单数据结构实现为一个索引数组;对邻接列表,则实现为一个链表。

    #include<iostream>
    #include<cstdlib>
    using namespace std;
    #define N 8
    struct node
    {
        int v;
        node *next;
        node(int x,node *t):v(x),next(t){ }
    };
    typedef node *Link;
    
    int main()
    {
        freopen("input.txt","r",stdin);
        int i,j;
        Link adj[N];
        for(i=0;i<N;i++) adj[i]=0;
        while(cin>>i>>j)
        {
            adj[j]=new node(i,adj[j]);
            adj[i]=new node(j,adj[i]);
        }
    
        for(i=0;i<N;i++)
        {
            cout<<i<<"\t";
            while(adj[i]->next!=NULL)
            {
                cout<<adj[i]->v<<"--->";
                adj[i]=adj[i]->next;
            }
            cout<<adj[i]->v<<endl;
    
        }
    
    
    
    }

    输出的语句是我自己加的。(algorithms in c++ parts1-4本没有)。看下面图:

    从链表中我们要注意,前面的0,1,2,3,4只是数组中的序号,并不是节点。对于无向图,如果在i的链表中存在节点j,则在j的链表中必定存在节点i。数组的第i个位置,包含了一个链表指针,链表中为每个和i链接的顶点保存一个节点。

    程序运行过程如下:

    输入 0 1

    adj[1]=new node(0,NULL); //为数组的第1个位置建立0节点

    adj[0]=new node(1,NULL); 为数组的第0个位置建立1节点

    输入 0 2

    adj[2]=new node(0,adj[2]) //为数组的第2个位置建立0节点

    adj[0]=new node(2,adj[0]); //这里很重要,adj[0]不是NULL了,代表了1节点,新建立的2节点后面的next为1节点说明我们建立链表的顺序是从右向左的

    所以我们输出时,2应该在前,1在后。

    我们输入:

    0 1
    0 2
    0 5
    0 6
    0 7

    5 4
    4 6

    5 3
    4 3

    4 7

    1 7
    2 7

     输出:

    可以看到,在数组的第0个位置,2在1前面。注意;邻接链表的输出与输入有关系。所以输出不是唯一的。

      我们现在顶点从1开始而不是从0开始:

    #include<iostream>
    #include<cstdlib>
    using namespace std;
    #define N 8
    typedef struct Node
    {
        int data;
        Node *next;
        Node(int x,Node *t)
        {
            data=x;
            next=t;
        }
    
    }*LinkNode;
    typedef struct{
        LinkNode *adj;
        int vexnum,arcnum;
        int kind;//图的种类标志
    }Graph;
    
    void DFS(Graph G,int v);
    
    static bool visited[100];
    void (*visitFunc)(int v);
    
    void visit(int v)
    {
        cout<<v<<ends;
    }
    void DFSTraverse(Graph G,void (*Visit)(int v))
    {
        visitFunc=Visit;
        for(int v=1;v<=G.vexnum;v++) visited[v]=false;
        for(int v=1;v<=G.vexnum;v++)
        {
            if(!visited[v])
                DFS(G,v);
        }
        
    }
    
    void DFS(Graph G,int v)
    {
        visited[v]=true;
        visitFunc(v);
        LinkNode m,n;
        m=G.adj[v];
        while(m!=NULL )
        {
            if(!visited[m->data])
                DFS(G,m->data);
            m=m->next;
        }
    }
    
    int main()
    {
        freopen("有向图邻接表.txt","r",stdin);
     
        //从1开始
        Graph graph;
        int vexnum,arcnum;
        cin>>vexnum>>arcnum;
        graph.vexnum=vexnum;
        graph.arcnum=arcnum;
    
        LinkNode *adj=new LinkNode[graph.vexnum+1];
        for(int i=1;i<=vexnum;i++) adj[i]=0;
    
        int start,end;//起点,终点
        for(int i=1;i<=arcnum;i++)
        {
            cin>>start>>end;
            adj[start]=new Node(end,adj[start]);
            adj[end]=new Node(start,adj[end]);
        }
        LinkNode *adj2=new LinkNode[graph.vexnum+1];
        for(int i=1;i<=vexnum;i++)
        {
            adj2[i]=adj[i];
        }
    
        for(int i=1;i<=vexnum;i++)
        {
            cout<<i<<"\t";
            while(adj[i]->next!=NULL)
            {
                cout<<adj[i]->data<<"--->";
                 adj[i]=adj[i]->next;
            }
    
           cout<<adj[i]->data<<endl;
        }
        //必须重置adj[i];因为现在adj[i]已经指向的最后一个节点,为了重置,之前必须保存adj[i]
        for(int i=1;i<=vexnum;i++)
        {
            adj[i]=adj2[i];
        }
    
        for(int i=1;i<=vexnum;i++)
        {
            cout<<i<<"\t";
            cout<<adj[i]->data<<endl;
        }
        
        graph.adj=adj;
        DFSTraverse(graph,visit);
    
    
    }

    按严蔚敏书上P168的图,输入:

    8
    9

    1 2 

    1 3

    2 4 

    2 5

    3 6 
    3 7

    4 8

    5 8

    6 7

    输出:

    我们按照严蔚敏书上关于邻接表的严格定义,写出如下代码:

    #include<iostream>
    using namespace std;
    typedef int InfoType;
     typedef  struct ArcNode{
         int adjvex;//该狐所指向的顶点位置
         ArcNode *nextarc;//指向下一条弧的指针
         InfoType *info;
     }ArcNode;
     typedef char   VextexType[10] ;
     typedef struct VNode{
         VextexType data; //顶点信息
         ArcNode *firstarc; //指向第一条依附该顶点的弧的指针
     }VNode,Adj[100];
     typedef  struct {
         Adj vertices;
         int vexnum,arcnum;
         int kind;
     }Graph;
     int locateVex(Graph &G,VextexType u)
     {
         for(int i=0;i<G.vexnum;i++)
          if(strcmp(u,G.vertices[i].data)==0)//如果VexTexType 为char []类型
            //if(u==G.vertices[i].data)//如果VextexType为数值型,用这句
                 return i;
         return -1;
     }
     void createGraph(Graph &G)
     {
         cout<<"请输入图的类型:有向图0,有向网1,无向图2,无向网3:"<<endl;
         cin>>G.kind;
         cout<<"输入顶点数和边数"<<endl;
         cin>>G.vexnum>>G.arcnum;
    
         cout<<"请输入"<<G.vexnum<<"个顶点的值\n";
         for(int i=0;i<G.vexnum;i++)//构造顶点向量
         {
             cin>>G.vertices[i].data;
             G.vertices[i].firstarc=NULL;
         }
    
         if(G.kind==1||G.kind==3)//
             cout<<"请输入每条弧(边)的权值、弧尾和弧头(以空格作为间隔):\n";
         else //
             cout<<"请输入每条弧(边)的弧尾和弧头(以空格作为间隔):\n";
         VextexType va,vb;
         int w;
         for(int k=0;k<G.arcnum;k++)
         {
             if(G.kind==1||G.kind==3)
                 cin>>w>>va>>vb;
             else
                 cin>>va>>vb;
    
             int i=locateVex(G,va);//弧尾
             int j=locateVex(G,vb);//弧头
             ArcNode *p=new ArcNode();
             p->adjvex=j;
              if(G.kind==1||G.kind==3)
              {
                  p->info=new int(w);
              }
              else
                  p->info=NULL;
    
              p->nextarc=G.vertices[i].firstarc;//插在表头
              G.vertices[i].firstarc=p;
    
              if(G.kind>=2) //无向的 ,产生第二个表节点
              {
                  p=new ArcNode();
                  p->adjvex=i;
                  if(G.kind==3) //无向网
                  {
                      p->info=new int(w);
                  }
                  else
                      p->info=NULL;
    
                  p->nextarc=G.vertices[j].firstarc;
                  G.vertices[j].firstarc=p;
    
              }
         }//end for
          
     }
              
    
    
     int main()
     {
         freopen("邻接表2.txt","r",stdin);
         Graph G;
         createGraph(G);
         for(int i=0;i<G.vexnum;i++)
         {
             cout<<G.vertices[i].data<<"\t";
              ArcNode *p=G.vertices[i].firstarc;
             while(p!=NULL)
             {
                 cout<<G.vertices[p->adjvex].data<<"--->";
                 p=p->nextarc;
             }
             cout<<endl;
    
         }
     }

    Adj vertices[i]存储的第i个顶点的信息,是一个结构体,里面的data该顶点的数据,firstarc表示指向的第一条狐。

    该程序同前面的程序实际上都是一样,都是在之前插入

    2 //无向图

    8
    9

    v1 v2 v3 v4 v5 v6 v7 v8

    v1 v2
    v1 v3

    v2 v4
    v2 v5

    v3 v6
    v3 v7

    v4 v8

    v5 v8

    v6 v7

    可以看到更 前面的一样。

    只不过这种程序灵活性更大。

    DFS遍历代码:

     bool visited[100];
    void (*visitFunc)(VextexType v);
    
    void visit(VextexType v)
    {
        cout<<v<<ends;
    }
    void DFS(Graph G,int v);//函数声明
    void DFSTraverse(Graph G,void (*Visit)(VextexType v))
    {
        visitFunc=Visit;
        for(int v=0;v<G.vexnum;v++) visited[v]=false;
        for(int v=0;v<G.vexnum;v++)
        {
            if(!visited[v])
                DFS(G,v);
        }
        
    }
    
    void DFS(Graph G,int v)
    {
        visited[v]=true;
        visitFunc(G.vertices[v].data);
        ArcNode *p=G.vertices[v].firstarc;
        while(p!=0)
        {
            int w=p->adjvex;
            if(!visited[w])
                DFS(G,w);
            p=p->nextarc;
        }
    }

    遍历上面的图输出:

    v1 v3 v7 v6 v2 v5 v8 v4。

    有几点值得注意,

     visitFunc(G.vertices[v].data);不是visitFunc(v).
    因为我们不是遍历顶点的序号,而是得到顶点的信息

    测试有向网:

    1                                                                                   

    4 5
    a b c d

    3 a b
    2 a c
    2 b c

    4 b d
    5 d c

                                                                                                

  • 相关阅读:
    MyBatis笔记:xml映射文件
    MyBatis笔记:xml配置文件
    JSP获取当前系统时间并显示
    使用<jsp:forward>和<jsp:param>
    JSP简单总结
    网页版学生管理系统简易版DOM
    当为servlet配置时出现servlet标签报错
    给js的事件驱动函数添加快捷键
    js的表格对象和DOM联合操作
    Centos7安装Greenplum5.3单机版教程
  • 原文地址:https://www.cnblogs.com/youxin/p/2613362.html
Copyright © 2020-2023  润新知