《数据结构》学习笔记-第六章 图(非线性结构)

1.基本术语

mNzZG4ubmV0L3FxXzQ0MTIyMzEy,size_16,color_FFFFFF,t_70)

2.表示和实现

2.1 两种表示方式

在这里插入图片描述

2.2 实现

Graph模板类:

template <typename Tv,typename Te> class Graph{//顶点类型,边类型  
 private:
  int n;//顶点 
  int e;//边 
  void reset(){//所有顶点、边的辅助信息复位
   for(int i=0;i<n;i++){ 
    status(i)=UNDISCOVERED;//顶点的状态信息 
    dTime(i)=fTime(i)=-1;//顶点的时间标签 
    parent(i)=-1;//父节点信息 
    priority(i)=INT_MAX;//优先级 
    for(int j=0;j<n;j++)//边
     if(exisits(i,j)) //判断两个顶点是否存在联边 
      status(i,j)=UNDETERMINED;//设置边的状态    
   }
  } 
 public:
  /*...顶点操作、边操作、图算法...无论如何实现,接口必须统一!*/
};

想要实现这些接口,就必须先把一个图给表示出来,在基本术语里已经知道图是由顶点和边组成的,所以先将顶点和边类实现

Vertex顶点类:

typedef enum{UNDISCOVERED,DISCOVERED,VISTED} VStatus;
template <typename Tv> struct Vertex{//顶点对象(并未严格封装) 
 Tv data;int inDegree,outDegree;//数据,出入度数
 VStatus status;//状态
 int dTime,fTime;//时间标签,记录顶点被发现以及访问完毕的时刻 
 int parent;//在遍历所生成的遍历树中的父节点 
 int priority;//基于优先级的遍历算法中,记录在遍历树中的优先级(最短通路,极短跨边等)
 Vertex(Tv const & d)://构造新顶点
  data(d),  inDegree(0),outDegree(0),status(UNDISCOVERED),
  dTime(-1),fTime(-1),  parent(-1),  priority(INT_MAX){} 
};

Edge边类:

ypedef enum{UNDETERMINED,TREE,CROSS,FORWARD,BACKWARD} EStatus; 
template <typename Te> struct Edge{//边对象(并未严格封装) 
 Te data;//数据
 int weight;//权重
 EStatus status;//类型
 Edge(Te const & d,int w)://构造新边
  data(d),weight(w),status(UNDISCOVERED){}
};

2.2.1 基于Adjacency_Matrix 邻接矩阵(空间利用率极低)

在这里插入图片描述

template <typename Tv,typename Te> class GraphMatrix:public Graph<Tv,Te>{//邻接矩阵 
 private:
  vector<Vertex<Tv>> V;//顶点集
  vector<vector<Edge<Te>*>> E;//边集
 public:
 /*操作接口:顶点相关、边相关*/
  GraphMatrix(){n=e=0;}//构造 
  ~GraphMatrix(){//析构
   for(int j=0;j<n;j++)
    for(int k=0;k<e;k++)
     delete E[j][k]; 
  } 
}; 

由于vector类对[ ]操作符的重载,所以可以很方便的对顶点和边操作

静态操作-顶点操作
在这里插入图片描述
除此之外,还要实现对于任意顶点枚举其所有的邻接顶点

 int nextNbr(int i,int j){//若已经枚举至邻居j,则转向下一个邻居
   while((-1<j)&&!exists(i,--j));//逆向查找 
   return j; 
  } 
 int firstNbr(int i){
   return nextNbr(i,n);//尽管n不存在,但是可以将其假象为一个哨兵 
  } 

静态操作-边操作
在这里插入图片描述
动态操作-边操作(矩阵规模不发生变化)

1.插入

  void insert (Te const & edge,int w,int i,int j){
   if(exists(i,j)) return;//忽略已有的边
   E[i][j]=new Edge<Te>(edge,w);//创建新边 
   e++;
   V[i].outDegree++;
   V[j].inDegree++;
  }

2.删除

  void remove (int i,int j){
   Te eBak=edge(i,j);//备份边
   delete E[i][j];
   E[i][j]=NULL;//删除边 
   e--;//更新边计数 
   V[i].outDegree--;
   V[j].inDegree--;
   return eBak; 
  }

动态操作-顶点操作(矩阵规模变化)

在这里插入图片描述
1.插入

  int insert(Tv const & vertex){//返回编号 
   for(int j=0;j<n;j++) E[j].insert(NULL);n++;//1
   E.insert( vector<Edge<Te>*>(n,n,NULL) );//2,3:E中插入一个记录边的行向量,总数为n 
   return V.insert(Vertex<Tv>(vertex));//4
  }

2.删除

  Tv remove (int i){//删除顶点及其联边,返回该顶点的信息
   for(int j=0;j<n;j++)
    if(exists(i,j)){//删除所有出边 
     delete E[i][j];
     V[j].inDegree--;
    } 
   E.remove(i); n--;//删除第i行 
   for(int j=0;j<n;j++)
    if(exists(j,i)){//删除所有入边及第i列 
     delete E[j].remove(j); V[j].outDegree--;
    }
   Tv vBak=vertex(i); 
   V.remove(i);//删除顶点i 
   return vBak;
  }

2.2.2 基于Adjacency_List 邻接表

在这里插入图片描述

2.2.3 两种方式对比

在这里插入图片描述
在这里插入图片描述

3.遍历

3.1 Breadth-First Search BFS广度优先搜索

概念
在这里插入图片描述实现

template <typename Tv,typename Te> 
void Graph<Tv,Te>::BFS(int v,int & clock){
 queue<int> Q;
 status(v)=DISCOVERED;
 Q.enqueue(v);
 while(!Q.empty()){
  int v=Q.dequeue();
  dTime(v)=++clock;//取出队首顶点并 
  for(int u=firstNbr(v);-1<u;u=nextNbr(v,u)){//考察每一邻居 
   if(UNDISCOVERED==status(u)){
    status(u)=DISCOVERED;
    Q.enqueue(u);//发现该点
    status(v,u)=TREE;//引入树边 
    parent(u)=v;
   }
   else{
    status(v,u)=CROSS;//归于跨边 
   } 
  } 
  status(v)=VISITED;//至此,当前顶点访问完毕 
 }
} 

若一幅图存在多个连通域

template <typename Tv,typename Te> 
void Graph<Tv,Te>::bfs(int s){//s为起始点
 reset();
 int clock=0;int v=s; 
 do{//逐一检查所有顶点,一旦遇到尚未发现的顶点 
  if(UNDISCOVERED==status(v))
   BFS(v,clock);//即从该顶点出发启动一次BFS 
 }
 while(s != (v = (++v%n) ) );//按序号访问 
}

树/森林:
在这里插入图片描述

3.2 Depth-First Search DFS深度优先搜索

概念
在这里插入图片描述
实现(仔细揣摩老师说的实例!06-D-5)

template <typename Tv,typename Te> 
void Graph<Tv,Te>::DFS(int v,int & clock){
 dTime(v)=++clock;
 status(v)=DISCOVERED;//发现当前顶点 
 for(int u=firstNbr(v);-1<u;u=nextNbr(v,u)){//考察每一邻居,注意是要等邻居都遍历完毕才转交控制权
  switch(status(u)){
   case UNDISCOVERED://u尚未被发现,意味着支撑树可在此发展 
    status(v,u)=TREE; 
    parent(u)=v; 
    DFS(u,clock);//递归
    break;
   case DISCOVERED:
    status(v,u)=BACKWARD;//回向边 (后代指向祖先)
    break;
   default://看u和v谁更早被发现,分为前向边(祖先指向后代)或者跨边 (两个顶点之间没有祖先或者直系后代的血缘关系 )
    status(v,u)=dTime(v)<dTime(u)?FORWARD:CROSS; 
    break;
  } 
 } 
 status(v)=VISITED;//至此,当前顶点访问完毕 
 fTime(v)=++clock;//节点被访问完毕的时刻 
}

但是可能有的顶点并不能被访问到,这时需要在外层包装一层遍历所有顶点的循环

template <typename Tv,typename Te> 
void Graph<Tv,Te>::dfs(int s){
 reset();
 int clock=0;int v=s; 
 do{//逐一检查所有顶点,一旦遇到尚未发现的顶点 
  if(UNDISCOVERED==status(v))
   DFS(v,clock);//即从该顶点出发启动一次DFS 
 }
 while(s != (v = (++v%n) ) );//按序号访问 
}

关于括号引理:
在这里插入图片描述

3.3 应用

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值