数据结构_图 总结

一、图的逻辑结构
1、在线性表中,元素个数可以为零,称为空表;
在树中,结点个数可以为零,称为空树;
在图中,顶点个数不能为零,但可以没有边。
2、一般情况下,图中的路径不惟一。
3、连通分量:非连通图的极大连通子图称为连通分量。
1).含有极大顶点数;
2). 依附于这些顶点的所有边。

二、图的抽象数据类型定义
1、图的遍历操作
从图中某一顶点出发,对图中所有顶点访问一次且仅访问一次。

  • 选取遍历的起始顶点:从编号小的顶点开始

  • 在线性表中,数据元素在表中的编号就是元素在序列中的位置,因而其编号是唯一的;

  • 在树中,将结点按层序编号,由于树具有层次性,因而其层序编号也是唯一的;

  • 在图中,任何两个顶点之间都可能存在边,顶点是没有确定的先后次序的,所以,顶点的编号不唯一。

  • 为了定义操作的方便,将图中的顶点按任意顺序排列起来,比如,按顶点的存储顺序。

  • 多次调用从某顶点出发遍历图的算法,解决从某个起点始可能到达不了所有其它顶点的问题

  • 因图中可能存在回路,某些顶点可能会被重复访问。可附设访问标志数组visited[n] ,来避免遍历不会因回路而陷入死循环

  • 在图中,一个顶点可以和其它多个顶点相连,当这样的顶点访问过后,使用深度优先遍历和广度优先遍历,选取下一个要访问的顶点
    2、图的存储结构及其实现
    基本思想:

  • 用一个一维数组存储图中顶点的信息

  • 用一个二维数组(称为邻接矩阵)存储图中各顶点之间的邻接关系。
    在这里插入图片描述
    3、构造函数

template <class T>
MGraph::MGraph(T a[ ], int n, int e) {
//确定图的顶点个数和边的个数:
    vertexNum=n; arcNum=e;
    for (i=0; i<vertexNum; i++) 
    //输入顶点信息存储在一维数组vertex中:
        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++) {
    //依次输入每条边存储在邻接矩阵arc中:
    //输入边依附的两个顶点的序号i, j:
        cin>>i>>j;     //边依附的两个顶点的序号
        //将邻接矩阵的第i行第j列的元素值置为1:
        //将邻接矩阵的第j行第i列的元素值置为1:
        arc[i][j]=1;  arc[j][i]=1;  //置有边标志    
    }
}

4、深度优先遍历

int visited[MaxSize];
template <class T>
void MGraph::DFSTraverse(int v){
//访问顶点v:
     cout<<vertex[v]; visited [v]=1;
     for (j=0; j<vertexNum; j++)
     //从v的未被访问的邻接点中选取一个顶点w,从w出发进行深度优先遍历
         if (arc[v][j]==1 && visited[j]==0)
         //重复上述两步,直至图中所有和v有路径相通的顶点都被访问到
         //递归定义

:            DFSTraverse( j );
}

5、广度优先遍历

int visited[MaxSize];
template <class T>
void MGraph::BFSTraverse(int v){     
    front=rear=-1;   //假设采用顺序队列且不会发生溢出
    // 访问顶点v:
   int Q[MaxSize]; cout<<vertex[v]; visited[v]=1;  Q[++rear]=v; 
   //依次访问v的各个未被访问的邻接点v1, v2, …, vk:
    while (front!=rear)    {
         v=Q[++front];   
         for (j=0; j<vertexNum; j++)
         //分别从v1,v2,…,vk出发依次访问它们未被访问的邻接点,并使“先被访问顶点的邻接点”先于“后被访问顶点的邻接点”被访问。
         //直至图中所有与顶点v有路径相通的顶点都被访问到
            if (arc[v][j]==1 && visited[j]==0 ) {
                  cout<<vertex[j]; visited[j]=1; Q[++rear]=j;
            }
      }
}

6、增加一个顶点
在存储顶点的一维数组中插入该顶点的信息,
在邻接矩阵中插入一行、一列。

template <class T>
void MGraph<T>::InsertVex(int num,T name) { 
 if ( num<0|| num>vertexNum) throw "位置";     
 int row, col, numv; 
 numv = vertexNum-1;
vertexNum++;    
for(int i=numv;i>=num;i--)	vertex[i++]=vertex[i];  
vertex[num]=name;    
 for(row=numv;row>=0;row--)  {所有行上num列之后的列后移,增加一列,
    for(col=numv;col>=num;col--)  arc[row][col+1]=arc[row][col];
     arc[row][num]=0;
  }
  for(row=numv;row>=num;row--) 
        for(col=0;col<=numv+1;col++)  arc[row+1][col]=arc[row][col];	
  for(col=0;col<vertexNum;col++)  arc[num][col]=0; 
 }

7、删除一个顶点
在存储顶点的一维数组中删除该顶点的信息
在邻接矩阵中删除一行、一列

template <class T>   void MGraph<T>::DeleteVex(int pos){
     if ( pos<0||  pos>MaxSize) throw "位置";   
     int row, col;    
     int numv=vertexNum;    
     for(int i=pos;i<numv;i++)   vertex[i]=vertex[i+1];    
     vertexNum--;                            
     for(row=0;row<numv;row++)   { //删除一列
         for(col=pos;col<numv;col++)	  arc[row][col]=arc[row][col+1];  
    }
    for(row=pos;row<numv;row++) 
	  for(col=0;col<numv;col++)
		  arc[row][col]=arc[row+1][col];      
  } 
}

8、增加一条边
修改相应的矩阵元素的值

tmplate <class T>
void MGraph<T>::InsertArc(int i, int j)
{
  if ( i>MaxSize||  j>MaxSize) throw "位置";  
  arc[i][j]=1;
  arc[j][i]=1;
} 

9、删除一条边
修改相应的矩阵元素的值

template <class T>
void MGraph<T>::DeleteArc(int i, int j)
{
         if ( i>MaxSize||  j>MaxSize) throw "位置";
 
         arc[i][j]=arc[j][i]=0;   
}

10、邻接表
邻接表有两种结点结构:顶点表结点和边表结点。在这里插入图片描述
vertex:数据域,存放顶点信息。
firstedge:指针域,指向边表中第一个结点。
adjvex:邻接点域,边的终点在顶点表中的下标。
next:指针域,指向边表中的下一个结点。
1)、定义邻接表结点
在这里插入图片描述
struct ArcNode{
int adjvex;
ArcNode *next;
};
在这里插入图片描述
template
struct VertexNode{
T vertex;
ArcNode *firstedge;
};
2)、构造函数

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;      
    } 

3)、深度优先遍历

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;           
    }
}

4)、广度优先遍历

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;
       }
    }
}

三、最小生成树
1、普里姆(Prim)算法

  • 数组lowcost[n]:用来保存集合V-U中各顶点与集合U中顶点最短边的权值,lowcost[v]=0表示顶点v已加入最小生成树中;
  • 数组adjvex[n]:用来保存该边所依附的(集合V-U中各顶点与集合U中顶点的最短边)集合U中的顶点。
void prime(MGraph G){
    for(int i=1;i<G.vertexNu;i++){
    // 初始化两个辅助数组lowcost(=arc[0][i])和adjvex(=0)(0是始点):
        lowcost[i]=G.arc[0][i];  adjvex[i]=0;
    }
    lowcost[0]=0;
    //输出顶点u0,将顶点u0加入集合U中:
    for(i=1;i<G.vertexNum;i+++){
        k=MinEdge(lowcost,G.vertexNum)
        cout<<K<<adjvex[k]<<lowcost[k];
        lowcost[k]=0;
        // 重复执行下列操作n-1次:
       for(j=1;j<G.vertexNum;j++)
       //在lowcost中选取最短边(lowcost[k]),取对应的顶点序号k:
       //输出顶点k和对应的权值:
       //将顶点k加入集合U中(lowcost[k]=0):
       //调整数组lowcost和adjvex:
          if((G.arc[k][j]<lowcost[j]){
              lowcost[j]=G.arc[k][j];
              arcvex[j]=k;
           }
    }
}

2、克鲁斯卡尔(Kruskal)算法

  1. 初始化:U=V; TE={ };
  2. 循环直到T中的连通分量个数为1
    2.1 在E中寻找最短边(u,v);
    2.2 如果顶点u、v位于T的两个不同连通分量,则
    2.2.1 将边(u,v)并入TE;
    2.2.2 将这两个连通分量合并为一个;
    2.3 在E中标记边(u,v),使得(u,v)不参加后续最短边的选取;
int main(){
    int arcNum, int vertexNum;
    EdgeNode *edge;
    int *parent;

    cout<<"please input the number of vertexNum:"; cin>>vertexNum;
    cout<<"please input the number of edges:";	cin>>arcNum;
    edge=new EdgeNode[arcNum];	parent=new int[vertexNum];
    for(int i=0;i<arcNum;i++)	{
 	cout<<"Please input the edges:";
	cin>>edge[i].from>>edge[i].to>>edge[i].weight;
    }
    sort(edges, G); //对边集数组进行堆排序,时间复杂性为O(eloge)
    for (i=0;i<vertexNum;i++)
	parent[i]=-1;  //每个节点分属于不同的集合

    int k=0,begin,end,count=0;
    cout<<"next is the MST :"<<endl;
	  for (k=0;k<arcNum;k++)	{
         begin=edge[k].from;	end=edge[k].to;	
         int m,n;
        m=Find(parent,begin);	n=Find(parent,end);
        if(m!=n)	{
            cout<<begin<<","<<end<<","<<edge[k].weight<<endl;
            parent[n]=m;	
            count++;
            if(count==vertexNum-1)	break;
       }
   }
   return 0;
}
int Find(int *parent, int node)
{
	int f;
	f=node;
	while(parent[f]>-1)
		f=parent[f];
	return f;
}

四、最短路径
1、Dijkstra算法

  • 图的存储结构:邻接矩阵存储结构

  • 数组dist[n]:每个分量dist[i]表示当前所找到的从始点v到终点vi的最短路径的长度。
    初态为:若从v到vi有弧,则dist[i]为弧上权值;否则置dist[i]为∞。

  • 数组path[n]:path[i]是一个字符串,表示当前所找到的从始点v到终点vi的最短路径。初态为:若从v到vi有弧,则path[i]为vvi;否则置path[i]空串。

  • 数组s[n]:存放源点和已经找到最短路径的终点,其初态为只有一个源点v。

const int MAX=1000;
void  Dijkstra(MGraph g, int v){
        for ( i =0; i<g.vexnum ; i++){
		dist[i]=g.arcs[v][i];  
        if ( dist[i]!= MAX) 
             path [i]=g.vertex[v]+g.vertex[i];
             else
             path[i]=“”;
       }
       S[0]=g.vertex[v]; 
       num=1;  
       while (num<g.vextexNum){
   		k=0;
	    for(i=0;i<G.vertexNum;i++)
	    if((dist[i]<dist[k])   k=i
	    cout<<dist[k]<<path[k];
	    s[num++]=G.vertex[k];                
	    for(i=0;i<G.vertexNum;i++)
        if(dist[k]+g.arc[k][i]<dist[i] {
		dist[i]=dist[k]+g.arc[k][i];
        path[i]=path[k]+g.vertex[i];
       }
	}
}

2、 Floyd算法
设图g用邻接矩阵法表示,求图g中任意一对顶点vi、vj间的最短路径。
(-1)将vi到vj 的最短的路径长度初始化为(vi,vj), 然后进行如下n次比较和修正:
(0)在vi、vj间加入顶点v0,比较(vi, v0, vj)和(vi, vj)的路径的长度,取其中较短的路径作为vi到vj的且中间顶点号不大于0的最短路径。
在这里插入图片描述

void Floyd(MGraph G)
{
		for (i=0; i<G.vertexNum; i++)        
        for (j=0; j<G.vertexNum; j++)
        {
          dist[i][j]=G.arc[i][j];
          if (dist[i][j]!=) 
               path[i][j]=G.vertex[i]+G.vertex[j];
          else path[i][j]=""; 
        }
        for (k=0; k<G.vertexNum; k++)         
        	for (i=0; i<G.vertexNum; i++)       
          		for (j=0; j<G.vertexNum; j++)
               		if (dist[i][k]+dist[k][j]<dist[i][j]) {
                    	dist[i][j]=dist[i][k]+dist[k][j];
                    	path[i][j]=path[i][k]+path[k][j];
             		}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值