黄梓财20191003015

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

第六章我们首先学习了图,知道图是由两个集合V(点)和E(边)组成,也知道了有向图和无向图的区别,同时也学习了用如何用邻接矩阵和领接表来表示图。

 

邻接矩阵:

方便检查任意一堆顶点间是否存在边,方便找任一顶点的所有“邻接点”,方便计算任一顶点的“度”(从改点出发的边数为出度,指向该点的为入度),但存稀疏图(点很多而边很少)有大量无效元素,会浪费空间。

const int MVNum=100;//最大顶点数
typedef char VerTexType;//假设顶点数据类型为char
typedef int ArcType;//假设边的权值为int

typedef struct{}
          VerTexType vexs[MVNum];//顶点表 char
           ArcType arcs[MVNum][MVNum];//邻接矩阵 int
           int vexnum,arcnum;//图当前的点数和边数
}AMGraph;

基于这个矩阵来建立 无向 带权图:

先输入图的点数和边数,再遍历每个顶点来输入点的信息,然后要先初始化每个边,将它们置于最大值,再遍历每个边,输入它们的权值,这样可以使得没有相连的两个点边的权值为最大值。

 

领接表:

方便找任一顶点的所以“邻接点”,方便计算任一顶点的度,

无向图:直接读G[i],访问这个链表,求出度
有向图:只能计算出度,要遍历其他顶点的领接点,来方便计算入度

const int MVNum=100;//最大顶点数
typedef char VerTexType;//假设顶点数据类型为char
typedef int ArcType;//假设边的权值为int

邻接点的结构
typedef struct ArcNode{
          int adjvex;//该边所指向的顶点的位置, 这个邻接顶点的 下标
          sturct ArcNode *nextarc;//指向下一个邻接顶点(也是指向下一个边)的指针
          OtherInfo info;//和边相关的信息,例如权值
}ArcNode;

顶点的结构
typedef struct{
        VexTexType data;//顶点信息 char
         ArcNode *firstarc;//指向第一条依附该顶点的弧 第一个邻接顶点
}VNode,AdjList[MVNUM];//AdjList就是定义数组 ,VNode是单个的名字

图的定义
typedef struct{
       AdjList vertices;//邻接表 ,是 一个数组
      int vexnum,arcnum;//顶点数和边数
}ALGraph;

基于这个矩阵来建立 无向 带权图:

与邻接矩阵基本一致,不过要将表头的指针域置为Null,且每次新的结点要申请空间,再不断使用头插法即可

 

还学到了一些一些连通图相关的定义

连通图:每个顶点都连通                 连通分量是:最大的连通子集,比如连通图的连通分量就是自己,非连通图的连通分量就是它的最大子集 

强连通:有向图中顶点v和w之间存在双向路径                强连通图:有向图中任意两点均强连通              强连通分量:有向图的极大强连通子图

 

 

学了图的构造后,就是学习图的遍历,遍历的方法有两种:深度优先搜索和广度优先搜索

深度优先搜索(DFS)
void DFS(Vextex V)
{
visited[v]=true;
for(v的每个邻接点w)
if(!visited[w]) //未被访问,则从它进行深度优先搜索
DFS(w);
}

广度优先搜索(BFS)
要运用队列,与树的层次遍历类似

void BFS(Vextex V)
{
       visited[v]=true;
        Q.push(v);
        while(!Q.empty())
         {
                   v = Q.front();

                  Q.pop();

                  for(V的每个邻接点w)
                  if(!visited[w])
                  {
                  visited[w]=true;
                 Q.push(w);
                 }
           }
}

但对于非连通图,这种方法无法完成遍历,要加入下面的代码,DFS与BFS都是一样的

void DFSTraverse(Graph G)
{
        for(v = 0;v<G.vexnum;++v)
        visited[v] = FALSE;//初始化visited数组
    for(v=0;v<G.vexnum;++v)
  if(!visited[v])    DFS(G,v);//对尚未访问的顶点调用DFS
}

 

但为了实际应用,我们还学了如何求有权图的单源最短路径

Dijkstra算法:令S={源点s + 已经确定了最短路径的顶点v1},对任一未收录的v,定义dist[v]为s到v的最短路径 长度,但该路径仅经过S中的顶点,{s 到 vi属于S 到 v}

也就是先取一点放于一个集合S中,然后再在其他点取一个与它带权路劲最小的点放于S中,再不断循环以上操作,从而求得有权图的单源最短路径


void Dijkstra(Vextex s )
{//自己到自己的权值也是为无穷,不能用0表示,而且这个算法不能解决权值为负的情况
while(1)
{//dist 是权值,  path是指向它的点,collocted数组就是表示是否在集合S中
           v = 未收录顶点中dist最小者;
          if(这样的v不存在) break;
          collected[v]=true;
            for(v的每个邻接点w)
             if(collected[w]=false)
               if(dist[v]++E<v,w> <dist[w])
           {
                dist[w] = dist[v] + E<v,w>;
                path[w] = v;
           }
}

}

 

最后学习的是最小生成树:在e条带权边中选n-1条边(不构成回路),使“权值之和”最小 。             一共有两个算法:普里姆算法算法,克鲁斯卡尔算法

普里姆算法算法与Dijkstra算法很类似,都是一个点集合,再不断从其他点选权值最小的点,但难点是要解决权值的变化,随着集合中点的增多,其他点的权值都在变

void MiniSoanTree_P(AMGraph G,VextexType u)
{//从顶点u出发构造网G的最小生成树
      k =LocataVex(G,u);
          for(j=0;j<G.vexnum;++j)//辅助数组初始化
      if(j!=k)
       colsedge[j] = {u,G.arcs[k][j].adj};
         closedge[k].lowcost = 0;//初始,U={0}
        for(i=1;i<G.vexnum;++i)
       {
        k = Min(closedge[]|closedge[].lowcost>0);//求出加入生成树的下一个顶点k
         cout << closedge[k].adjvex << G.vexs[k];//输出生成树上一条边 和 他的编号
          closedge[k].lowcost = 0;//第K顶点并入U集
          for(j=0;j<G.vexnum;++j)//修改其他顶点的最小边
       if(G.arcs[k][j]<closedge[j].lowcost)
          colsedge[j]={G.vexs[k],G.arcs[k][j]};
       }

}

克鲁斯卡尔算法:不断的选择权值最小的边

 

posted on 2020-06-14 18:07  黄梓财  阅读(160)  评论(0编辑  收藏  举报