图
程序员文章站
2022-05-23 19:43:50
...
概述
两点之间有关系则可以用一条边表示,即邻接关系 关联关系是指点与其边的关系
有向图无向图
点的连边没有次序关系即是无向边,均是无向边是无向图 还有有向图 混合图
因为无向边可以看成两个有向边,那么有向图的相关算法可以扩展到无向混合
路径/环路
简单路径:无重复节点 环/环路:起止节点相同 无环路就是有向无环路
经过每一条边一次:欧拉环路 经过每一个顶点恰好一次:哈密尔顿环路
邻接矩阵
Graph模板类
template <typename Tv,typename Te> class Graph
{
private:
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(exists(i,j) status(i,j)=UNDETERMINED);
}
}
public: ///顶点操作 边操作 图算法
}
邻接矩阵
关联矩阵 n点e边 ,n行e列;任一列只有两个为1
邻接矩阵
描述顶点间关系的矩阵,nn,有关系则取作1;;无向图关系矩阵对称 ,对称线上自环; 关系权重weighted
Vertex 顶点类实现
typedef enum {UNDISCOVERED,DISCOVERRED,VISITED} 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(a),inDegree(0),outDegree(0), status(UNDISCOVERED),
dTime(-1),fTime(-1), parent(-1),
priority(INT_MAX){}
};
Edge
typedef
enum{UNDETERMINED,TREE,CROSS,FORWAED,BACKWARD}
EStatus;
template <typename Te> struct Edge
{
Te data;//数据
int weight;//权重
EStatus status;//类型
Edge(Te const &d,int w)://构造新边
data(d),weight(w),status (UNDETERMINED){}
};
GraphMatrix
template <typename Tv,typename Te> class GraphMatrux: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<n;k++)
delete E[j][k];//[] 已重构,清楚动态申请的边记录
}
};
顶点静态操作
返回一些顶点信息;
在遍历过程中,对于顶点i 如何枚举所有邻接顶点neighbor
int nextBbr (int i,int j) {
while((-1<j)&&!exists(i,--j));//逆向顺序查找
return j;
} //改用邻接表可提高至O(1+outDegree(i))
int firstBnr(int i)
{return nextNbr(i,n);//n 假象哨兵
}//首个数据
边操作
bool exists(int i,int j)//判断(i,j)是否存在联系
{
return (0<=i)&&(i<n)&&(0<=j)&&(j<n)&&E[i][j]!=NULL; //短路求值
}//以下假定exists(i,j)
Te & edge(int i,int j)//边(i,j)的数据
{ return E[i][j]->data}//O(1)
**插入一条边** 将要插入信息封装插入
void insert (Te const & edge,int w,int i,int j)
{
if(exists(i,j)) retur;//忽略已有的边
E[i][j]= new Edge<Te>(edge,w);//创建新边
e++;//更新边数
V[i].outDegree++;//更新关联顶点i的出度
V[j].inDegree++;
}
**边删除**释放边记录,引用为空
Te remove(int i,int j)
{
Te eBak=edge(i,j);//备份信息
delete E[]i[j];E[i][j]=NULL;//删除边(i,j)
e--;
V[i].outDegree--;//更新关联顶点i的出度
V[j].inDegree--;
return eBak;
}
顶点动态操作
顶点插入 对于矩阵增加行单元即增加一列,增加一行 第一级编表增加一个相应单元,,顶点向量增加相应元素
int insert(Tv const &vertex)
{
for(int j=0;j<n;j++) E[j].insert(NULL); n++;
E.insert(Vector<Edge<Te>*> n,n,NULL);
return V.insert(Vertex<Tv>(vertex));
}
顶点删除
Tv remove (int i)
{
for(int j=0;j<n;j++)
if(exist(i,j))//删除所有出边
{ delete E[i][j];V[j].inDgree--;}
E.remove(i); n--;//删除第i行
for(int j=0;j<n;j++)
if(exists(j,i))//删除所有入边及第i列
{ delete E[j].remove(i); V[j].outDegree--; }
Tv VBak=vertex(i);//备份顶点i的信息
V.remove(i);//删除顶点i
return vBak;//返回被删除顶点的信息
}
缺点:消耗准确的n^2空间,与边数无关; 平面图O(n),空间利用率1/n
广度优先搜索Breadth-First_Search(将非线性转成半线性)
从顶点s开始,访问它的邻接顶点,已访问的顶点有连边则忽略,迭代访问连接顶点,直到访问完所有节点(又叫支撑树) 与树的层次遍历有异曲同工之妙
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=firstBbr(v);-1<u;u=nextNbr(v,u))//考察v的每一个邻居u
{
if(UNDISCOVERED==status(u))//如果u尚未被发现
{
status(u) =DISCOVERED;Q.enqueue(u);//发现该顶点(修改状态)并入队
status(v,u)=TREE;parent(u)=v;//修改成采纳状态,引入树边
}else //若u正在队列或已出队列
status(v,u)=CROSS;//将(v,u)归类于跨边
}
status(v)=VISITED;//修改状态,至此访问完毕
}
}
上一篇: JS中广度深度优先遍历应用拷贝函数
下一篇: js --- 图的深度广度优先遍历