概述

两点之间有关系则可以用一条边表示,即邻接关系 关联关系是指点与其边的关系
有向图无向图
点的连边没有次序关系即是无向边,均是无向边是无向图 还有有向图 混合图
因为无向边可以看成两个有向边,那么有向图的相关算法可以扩展到无向混合
路径/环路
简单路径:无重复节点 环/环路:起止节点相同 无环路就是有向无环路
经过每一条边一次:欧拉环路 经过每一个顶点恰好一次:哈密尔顿环路

邻接矩阵

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;//修改状态,至此访问完毕
	}
}

复杂度: inqueue执行n次,那么dequeue执行n次 即while执行n次 内部for执行则扫描n次

多连通
当一个图含有多个连通域,那一个顶点可能无法遍历所有顶点,接下来又该怎么做遍历全部节点呢?

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))//累计为确定的n
			BFS(v,clock);//从该顶点出发启动一次BFS
	while(s!=(v=(++v%n)));
}//按序号访问,不重不漏

最短路径

深度优先搜索

DFS(s):访问顶点s,在尚未访问的邻居中选择一点继续执行DFS 若没有邻居,则上溯顶点访问其邻居
实现

template <typename Tv,typename Te>
void Graph<Tv,Te>::DFS(int v,int & clock)
{
	dTime(v)=++clock; status(v)=DISCOVERED;//发现当前顶点v
	for(int u=firstNbr(v); -1<u; u=nextNbr(v,u))//枚举每一个邻居
		switch*status(u)
		{
			case UNDISCOVERED://尚未发现,意味着支撑树可在此拓展
				status(v,u)=TREE;parent(u)=v; DFS(u,clock);break;//递归
			case DISCOVERED ://u已经被发现但尚未访问完毕,应属于被后代指向的祖先(后向边或回边)
				status(u,v)= BACKWARD;break;
			default: //u已访问完毕(VISITED,有向图),则视承袭关系分为前向边(祖先指向后代)或跨边
				status(v,u)=dTime(v)<dTime(u)? FORWARD:CROSS;break;
		}

	status(v)=VISITED; fTime(v)=++clock;
}

多可达域
从某一个顶点开始无法完全遍历图,再加上一个循环

嵌套引理
括号引理
顶点的活动期:active[u]=(dTime[u],fTime[u])
祖先后代关系与两者的活跃期有关,祖先的活跃期会覆盖后代的活跃期

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值