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

 图的邻接矩阵表示:

  下面的这个程序读入一组定义一个无向图的便,创建一个对应这个图的邻接矩阵。如果在图中顶点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

                                                                                            

《图的邻接矩阵和邻接链表表示》《图的邻接矩阵和邻接链表表示》

 

 

    原文作者:算法小白
    原文地址: https://www.cnblogs.com/youxin/archive/2012/07/28/2613362.html
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞