欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

程序员文章站 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;//修改状态,至此访问完毕
	}
}