图的存储结构及实现

邻接矩阵(数组表示法)

基本思想: 用一个一维数组存储图中顶点的信息,用一个二维数组(称为邻接矩阵)存储图中各顶点之间的邻接关系。

无向图的邻接矩阵

1、无向图的邻接矩阵的特点:
主对角线为0且一定是对称矩阵。

2、如何求顶点i的度?
邻接矩阵的第i行(或第i列)非零元素的个数。

3、如何判断顶点 i 和 j 之间是否存在边?
测试邻接矩阵中相应位置的元素arc[i][j]是否为1。

4、如何求顶点 i 的所有邻接点?
将数组中第 i 行元素扫描一遍,若arc[i][j]为1,则顶点 j 为顶点 i 的邻接点。

有向图的邻接矩阵

1、有向图的邻接矩阵一定不对称吗?
不一定,例如有向完全图。

2、如何求顶点 i 的出度?
邻接矩阵的第 i 行元素之和。

3、如何求顶点 i 的入度?
邻接矩阵的第 i 列元素之和。

4、如何判断从顶点 i 到顶点 j 是否存在边?
测试邻接矩阵中相应位置的元素arc[i][j]是否为1。

邻接矩阵存储无向图的类

const int MaxSize=10;
template<class T>
class Mgraph{    
public:       
 MGraph(T a[ ], int n, int e );           
 ~MGraph( )        
 void DFSTraverse(int v);         
 void BFSTraverse(int v);         
 ……
private:        
 T vertex[MaxSize];         
 int arc[MaxSize][MaxSize];         
 int vertexNum, arcNum;
}; 

邻接矩阵中图的基本操作——构造函数
MGraph(T a[ ], int n, int e );
1、确定图的顶点个数和边的个数;
2、输入顶点信息存储在一维数组vertex中;
3、初始化邻接矩阵;
4、依次输入每条边存储在邻接矩阵arc中;
4.1 输入边依附的两个顶点的序号i, j;
4.2 将邻接矩阵的第i行第j列的元素值置为1;
4.3 将邻接矩阵的第j行第i列的元素值置为1;

template<class T>
MGraph::MGraph(T a[ ], int n, int e) {     
 vertexNum=n; arcNum=e;     
 for (i=0; i<vertexNum; i++)          
  vertex[i]=a[i];     
 for (i=0; i<vertexNum; i++)
  for (j=0; j<vertexNum; j++)            
   arc[i][j]=0;
 for (k=0; k<arcNum; k++) {         
  cin>>i>>j;
  arc[i][j]=1;  
  arc[j][i]=1;
 }
}     

邻接矩阵中图的基本操作——深度优先遍历

⑴ 访问顶点v;
⑵ 从v的未被访问的邻接点中选取一个顶点w,从w出发进行深度优先遍历;
⑶ 重复上述两步,直至图中所有和v有路径相通的顶点都被访问到。

递归定义

int visited[MaxSize]; 
template<class T>
void MGraph::DFSTraverse(int v){      
 cout<<vertex[v]; visited [v]=1;      
 for (j=0; j<vertexNum; j++)          
  if (arc[v][j]==1 && visited[j]==0)             
   DFSTraverse( j );
}

邻接矩阵中图的基本操作——广度优先遍历
⑴ 访问顶点v;
⑵ 依次访问v的各个未被访问的邻接点v1, v2, …, vk;
⑶ 分别从v1,v2,…,vk出发依次访问它们未被访问的邻接点,并使“先被访问顶点的邻接点”先于“后被访问顶点的邻接点”被访问。直至图中所有与顶点v有路径相通的顶点都被访问到。

int visited[MaxSize];
template<class T> 
void MGraph::BFSTraverse(int v){          
 front=rear=-1;
 int Q[MaxSize]; 
 cout<<vertex[v]; 
 visited[v]=1;
 Q[++rear]=v;
 while (front!=rear){
  v=Q[++front];             
  for (j=0; j<vertexNum; j++)
   if (arc[v][j]==1 && visited[j]==0 ){                   
    cout<<vertex[j]; 
    visited[j]=1;
    Q[++rear]=j;             
   }
 } 
}

邻接矩阵上的其他操作
增加一个顶点
在存储顶点的一维数组中插入该顶点的信息,在邻接矩阵中插入一行、一列。
删除一个顶点
在存储顶点的一维数组中删除该顶点的信息,在邻接矩阵中删除一行、一列。
增加一条边
修改相应的矩阵元素的值。
删除一条边
修改相应的矩阵元素的值。
邻接表
假设图G有n个顶点e条边,则存储该图需要O(n2) 。
基本思想: 对于图的每个顶点vi,将所有邻接于vi的顶点链成一个单链表,称为顶点vi的边表(对于有向图则称为出边表),所有边表的头指针和存储顶点信息的一维数组构成了顶点表。
邻接表有两种结点结构:顶点表结点和边表结点。
定义邻接表的结点

struct ArcNode{          
 int adjvex;
 ArcNode *next;
};
template<class T> 
struct VertexNode{       
 T vertex;      
 ArcNode *firstedge;
};

无向图的邻接表

1、边表中的结点表示什么:
每个结点对应图中的一条边, 邻接表的空间复杂度为O(n+e)。

2、如何求顶点 i 的度?
顶点i的边表中结点的个数。

3、如何判断顶点 i 和顶点 j 之间是否存在边?
测试顶点 i 的边表中是否存在终点为 j 的结点。

有向图的邻接表(出边表)

1、如何求顶点 i 的出度?
顶点 i 的出边表中结点的个数。

2、如何求顶点 i 的入度?
各顶点的出边表中以顶点 i 为 终点的结点个数。

3、如何求顶点 i 的所有邻接点?
遍历顶点 i 的边表,该边表中的所有终点都是顶点 i 的邻接点。

有向图的逆邻接表(入边表)

为方便的计算有向图的顶点的入度,可以构造逆邻接表。 在逆邻接表中,边表中存储的是以顶点vi为弧头的弧。

邻接表存储有向图的类

const int MaxSize=10;
template<class T>
class ALGraph {        
public:        
 ALGraph(T a[ ], int n, int e);           
 ~ALGraph;            
 void DFSTraverse(int v);              
 void BFSTraverse(int v);
 ………   
private:        
 VertexNode adjlist[MaxSize];           
 int vertexNum, arcNum;        
};     

邻接表中图的基本操作——构造函数

1、确定图的顶点个数和边的个数;
2、输入顶点信息,初始化该顶点的边表;
3、依次输入边的信息并存储在边表中;

3.1 输入边所依附的两个顶点的序号i和j;
3.2 生成邻接点序号为j的边表结点s;
3.3 将结点s插入到第i个边表的头部;

template<class T> 
ALGraph::ALGraph(T a[ ], int n, int e) {        
 vertexNum=n; 
 arcNum=e;      
 for (i=0; i<vertexNum; i++)        
 {        
  adjlist[i].vertex=a[i];        
  adjlist[i].firstedge=NULL;           
 }
 for (k=0; k<arcNum; k++)         
 {          
  cin>>i>>j;              
  s=new ArcNode; 
  s->adjvex=j;                     
  s->next=adjlist[i].firstedge;              
  adjlist[i].firstedge=s;      
 } 
}

邻接表中图的基本操作——深度优先遍历

template<class T>
void ALGraph::DFSTraverse(int v){             
 cout<<adjlist[v].vertex;  
 visited[v]=1;     
 p=adjlist[v].firstedge;         
 while (p!=NULL){         
  j=p->adjvex;         
  if (visited[j]==0) 
   DFSTraverse(j);     
  p=p->next;
 }
}

邻接表中图的基本操作——广度优先遍历

template<class T> 
void ALGraph::BFSTraverse(int v){    
 front=rear=-1;       
 cout<<adjlist[v].vertex;    
 visited[v]=1;   
 Q[++rear]=v;       
 while (front!=rear){        
  v=Q[++front];    
  p=adjlist[v].firstedge;
  while (p!=NULL){             
   j= p->adjvex;             
   if (visited[j]==0){                 
    cout<<adjlist[j].vertex;  
    visited[j]=1; 
    Q[++rear]=j;             
   }             
   p=p->next;        
  }     
 } 
}

插入边

template <class T>
void ALGraph<T>::InsertArc(int i, int j)
{
    if ( i>MaxSize || j>MaxSize)
        throw "位置";
    ArcNode *s=new ArcNode;
    s->adjvex=j;
    s->next=adjlist[i].firstedge;
    adjlist[i].firstedge=s;
}

删除边

template <class T>
void ALGraph<T>::DeleteArc(int i, int j)
{
    if ( i>MaxSize||  j>MaxSize)
        throw "位置";
    ArcNode *s;
    ArcNode *pre;
    s = adjlist[i].firstedge;
    if (!s)
        return;
    if(s->adjvex==j)
    {
        adjlist[i].firstedge=s->next;
        return;
    }
    pre = adjlist[i].firstedge;
    while(s!=NULL && s->adjvex!=j)
    {
        pre = s;
        s = s->next;
    }
    if(s!=NULL)
    {
        pre->next = s->next;
        delete s;
    }
}

插入顶点

template <class T>
void ALGraph<T>::InsertVex( T value)
{
    if ( i>vertexNum || i<0 || i>MaxSize )
        throw "顶点位置异常";
    vertexNum++; 
    VertexNode<T> tempvertex;
    tempvertex.vertex = value;
    tempvertex.firstedge = NULL;
    adjlist[VertexNum-1] = tempvertex;
}

边集数组的实现

Struct edge
{ 
    int i;
    int j;
    int weight;
}

将邻接矩阵转化成边集数组

edge edges[M];//边的数据结构类型的变量
 for ( i = 0; i < G->vexnum; i++) { 
  for (j = 0; j <= G->vexnum; j++)  {
     if (G->arc[i][j] == 1)   {
        edges[k].begin = i;
          edges[k].end = j;
           // edges[k].weight = G->arc[i][j];
               k++;
         }
     }
 }
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值