6.1 Graph(图)

6.1 Graph(图)

参考博主ripplesding

本章问题

1.在图的模板中BFS和DFS与在OJ做算法题有什么不同

在写题是主要开一个标记数组,标记遍历的节点,而在图的模板中使用的结构体,是否遍历是用该节点的属性表示的。

2.拓扑排序有什么特点

实现步骤:
1.确立节点的入度和出度,将所节点入队
2.入度为0的节点出队,更新关联节点的入队,然后入度为0的节点出队
3.更新关联节点的入度
之后从第2步不断循环,如果队列能为空,那么就可以拓扑排序
图中的拓扑排序不唯一
拓扑排序存在当且仅当图为向无环图(DAG)

3.怎么在一个图上得到一个欧拉回路

首先: 确定能否得到欧拉回路,图必须满足条件,无奇度顶点且联通。
拓展: 欧拉通路,有且仅有两个奇度顶点且联通
实现步骤: 从任一顶点出发做一趟DFS,依次经历沿途经过的各边并随即将其从图中删除; 一旦有顶点度数归0,则随即将其从图中删除。每当回到起点,则得到一条欧拉子回路。此时若存在已访问但未删除的顶点,则任选其一并从中出发,再做一趟DFS,过程相同。每次所新得的子环路,都需要在搜索的起始点处与此前的子环路合并为一条更大的子环路。最终不剩任何顶点时,算法结束,当前的子环路即为原图的欧拉环路。

4.实现prim和Dijkstra算法和Kruskal算法

图的一般搜索框架:都是通过迭代逐一发现各顶点,将其纳入遍历树中,并做相应的处理。各个算法在功能上的差异,主要体现为每一步迭代中对新顶点的选取策略不同,BFS优先考查早发现的顶点,DFS优先考察最后被发现的顶点
prim算法描述:
1.在一个加权连通图中,顶点集合V,边集合为E
2.任意选出一个点作为初始顶点,标记为visit,加入顶点集V中,计算所有与顶点集中的点相连接的,最小距离的点,标记为visit.
3.重复以下操作,直到所有点都被标记为visit
在剩下的点钟,计算与已标记visit点距离最小的点,标记visit,证明加入了最小生成树。
Kruskal算法描述:
基本思想:
1.先构造一个只含 n 个顶点、边集为空的子图,把子图中各个顶点看成各棵树上的根结点
2.从网的边集 E 中选取一条权值最小的边,若该条边的两个顶点分属不同的树,则将其加入子图,即把两棵树合成一棵树,反之,若该条边的两个顶点已落在同一棵树上,则不可取,而应该取下一条权值最小的边再试之。
3.依次类推,直到森林中只有一棵树,即子图中含有 n减1条边为止。
Dijkstra算法描述:
功能:计算单源最短路径
描述:
Vector存图表示 状态方程dis[i] = min(dis[i],dis[p] + mp[p][i])
邻接矩阵的状态方程 dis[start][i]=min(dis[start][i],dis[start][p] + mp[p][i])
起点到 i 点的代价,等于父节点到子节点代价的和,与当前节点的最小代价的,相比较的最小值

5.优先级遍历接口如何在prim中和Dijkstra确定节点的优先级

通过比较边的权值,来更新优先级别

6.什么是支撑树,什么是跨边(crossEdge)和树边

图G的一个支撑子图(spanning subgraph)是一个含有G的所有节点的子图。如果图G的支撑子图是一棵树,则称为G的支撑树(spanning Tree),或者称为生成树。我们通常说的最小生成树(minimal spanning tree)就是指图G的所有支撑树中边权之和最小的支撑树。
我们在做dfs的时候,当访问到一个节点时,会出现四种情况:
1.此节点未被访问过,则此次的访问关系边(发起点——>接受点)称为树边(tree edge);
2.此节点被访问过但此节点的子孙还没访问完,换句话说,此次的发起点的源头可以追溯到接收点,则此次访问关系边称为后向边(back edge);
3.此节点被访问过且此节点的子孙已经访问完,而且发起点是搜索初始边,则称为前向边(down edge);
4.此节点被访问过且此节点的子孙已经访问完,而且发起点不是搜索初始边,则称为横叉边(cross edge)。

6.1.1 ADT接口

操作接口(边) 功能描述 操作接口(顶点) 功能描述
e( ) 边总数 n( ) 顶点总数
exist(v,u) 判断联边(v,u)是否存在 insert(v ) 在顶点集V中插入新的顶点v
insert(v,u) 引入顶点v到u的联边 remove(v ) 将顶点v从顶点集中删除
remove(v,u) 删除从顶点v到u的联边 inDegreee( )
outDegree( )
顶点的入度和出度
type(v,u) 边在遍历树中所属的类型 firstNbr(v ) 顶点v的首个邻接节点
edge(v,u) 边所对应的数据域 nextNbr(v,u) 在v的邻接节点中,u的后继
weight(v,u) 边的权重 status(v ) 顶点v 的状态
dTime( v),fTime(v ) 顶点v的时间标签
parent(v ) 顶点v在遍历树中的父节点
priority(v ) 顶点v在遍历树中的权重

6.1.2 Graph模板类

#include"Stack.h"
typedef enum{
    
     UNDISCOVERED, DISCOVERED, VISITED} VStatus;
typedef enum{
    
     UNDETERMINED, TREE, CROSS, FORWARD, BACKWARD} EStatus;
template <typename Tv, typename Te> class Graph
{
    
    
private:
/*--内部成员--*/
void reset(){
    
      // 重置函数
    for(int i = 0; i < n; i++){
    
    
        status(i) = UNDISCOVERED;
        dTime(i) = f(time) = -1;
        parent(i) = -1; priority(i) = INT_MAX;  // 优先级位最大
        for(int j = 0; j < n; j++){
    
    
            if(exists(i, j)) type(i, j) = UNDISCOVERED;
        }
    }
}
void BFS(int ,int&);//(连通域)广度搜索
void DFS(int ,int&);//深度搜索
void BCC(int ,int &,Stack<int>&);//双连通域分解
bool Tsort(int ,int &,Stack<int>&);//拓扑排序
template <typename PU> void PFS(int, PU);//优先搜索框架
public:
/*--顶点操作--*/
int n;
virtual int insert(Tv const&) = 0;
virtual Tv remove(int) = 0;
virtual Tv& vertex(int) = 0;
virtual int inDegree(int) = 0;
virtual int outDegree(int) = 0;
virtual int firstNbr(int) = 0;
virtual int nextNbr(int, int) = 0;
virtual VStatus& status(int) = 0;
virtual int& dTime(int) = 0;
virtual int& fTime(int) = 0;
virtual int& parent(int) = 0;
virtual int& priority(int) = 0;
/*--边操作--*/
int e();
virtual bool exist(int, int) = 0;
virtual void insert(int v, int u, Te const& e, int w) = 0;
virtual Te remove(int, int) = 0;
virtual EStatus& status(int,  int) = 0;
virtual Te& edge(int, int) = 0;
virtual int& weight(int, int) = 0;
/*--算法--*/
void bfs(int);
void dfs(int);
template <typename PU>  void pfs(int, PU)

};

6.1.3 Graph类

#pragma once

#include <assert.h>
#include <limits.h> // INT_MAX
#include "queue.h"
#include "stack.h"

namespace dtl 
{
    
    

// 顶点状态
enum class VertexStatus {
    
    
	// 未发现
	undiscovered,
	// 已发现
	discovered,
	// 已访问
	visited,
};

// 边状态
enum class EdgeStatus {
    
    
	// 未处置
	undetermined,
	// 树边
	tree,
	// 跨边
	cross,
	// 前向边
	forward,
	// 后向边
	backward,
};

// class of GRAPH
template <typename Tv, typename Te>
class Graph
{
    
    
private:
	// 所有顶点、边的辅助信息复位
	void reset() {
    
    
		for (int i = 0; i < n; i++) {
    
    
			status(i) = VertexStatus::undiscovered;
			dTime(i) = fTime(i) = -1;
			parent(i) = -1;
			priority(i) = INT_MAX;
			for (int j = 0; j < n; j++) {
    
    
				if (exists(i, j))
					type(i, j) = EdgeStatus::undetermined;
			}
		}
	}

	// breadth first search
	void BFS(int v, int& clock) {
    
    
#if DSA_MODE
		printf("BFS in with v=%d, clock=%d\n", v, clock); 
#endif

		Queue<int> queue;
		status(v) = VertexStatus::discovered;
		queue.enqueue(v);

#if DSA_MODE
		printf("vetex v=%d", v); print(vertex(v)); printf(" status changed to VertexStatus::discovered\n");
		printf("current Q: "); print(queue);
#endif

		while (!queue.empty()) {
    
    
			int v = queue.dequeue();
			dTime(v) = ++clock;

#if DSA_MODE
			printf("visiting vertex v=%d", v); print(vertex(v)); printf("["); print(status(v)); printf("] dTime=%d\n", clock);
#endif

			for (int u = firstNbr(v); -1 < u; u = nextNbr(v, u)) {
    
    

#if DSA_MODE
				printf("visiting vertex u=%d", u); print(vertex(u)); printf("["); print(status(u)); printf("]\n");
#endif

				if (status(u) == VertexStatus::undiscovered) {
    
    
					status(u) = VertexStatus::discovered;
					queue.enqueue(u);
					type(v, u) = EdgeStatus::tree;
					parent(u) = v;
				} else {
    
    
					type(v, u) = EdgeStatus::cross;
				}

#if DSA_MODE
				printf("edge(%d, %d) ", v, u); print(vertex(v)); printf(" ->"); print(vertex(u)); printf(" now is "); print(type(v, u)); printf("\n");
#endif

			}
			status(v) = VertexStatus::visited;

#if DSA_MODE
			printf("vetex v=%d", v); print(vertex(v)); printf(" status changed to VertexStatus::visited\n");
			printf("current Q: "); print(queue);
#endif
		}
	}

	//! depth first search
	void DFS(int v, int& clock) {
    
    
#if DSA_MODE
		printf("DFS in with v=%d, clock=%d\n", v, clock);
#endif

		dTime(v) = ++clock;
		status(v) = VertexStatus::discovered;

#if DSA_MODE
		printf("vetex v=%d", v); print(vertex(v)); printf(" status changed to VertexStatus::discovered dTime=%d\n", clock);
#endif

		for (int u = firstNbr(v); -1 < u; u = nextNbr(v, u)) {
    
    

#if DSA_MODE
			printf("visiting vertex u=%d", u); print(vertex(u)); printf("["); print(status(u)); printf("]\n");
#endif

			switch (status(u)) {
    
    
				case VertexStatus::undiscovered:
					type(v, u) = EdgeStatus::tree;
					parent(u) = v;
					DFS(u, clock);
					break;
				case VertexStatus::discovered:
					type(v, u) = EdgeStatus::backward;
					break;
				default: // VertexStatus::visited
					type(v, u) = dTime(v) < dTime(u) ? EdgeStatus::forward : EdgeStatus::cross;
					break;
			}

#if DSA_MODE
			printf("edge(%d, %d) ", v, u); print(vertex(v)); printf(" ->"); print(vertex(u)); printf(" now is "); print(type(v, u)); printf("\n");
#endif
		}

		status(v) = VertexStatus::visited;
		fTime(v) = ++clock;

#if DSA_MODE
		printf("vetex v=%d", v); print(vertex(v)); printf(" status changed to VertexStatus::visited fTime=%d\n", clock);
#endif
	}

	//! topological sort
	bool TSORT(int v, int& clock, Stack<Tv>* s) {
    
    
		assert(0 <= v && v < n);

#if DSA_MODE
		printf("TSORT in with v=%d, clock=%d\n", v, clock);
		printf("statck="); print(s);
#endif
		
		dTime(v) = ++clock;
		status(v) = VertexStatus::discovered;

#if DSA_MODE
		printf("vetex v=%d", v); print(vertex(v)); printf(" status changed to VertexStatus::discovered dTime=%d\n", clock);
#endif

		for (int u = firstNbr(v); -1 < u; u = nextNbr(v, u)) {
    
    

#if DSA_MODE
			printf("visiting vertex u=%d", u); print(vertex(u)); printf("["); print(status(u)); printf("]\n");
#endif

			switch (status(u)) {
    
    
				case VertexStatus::undiscovered:
					parent(u) = v;
					type(v, u) = EdgeStatus::tree; 

#if DSA_MODE
					printf("edge(%d, %d) ", v, u); print(vertex(v)); printf(" ->"); print(vertex(u)); printf(" now is "); print(type(v, u)); printf("\n");
#endif

					if (!TSORT(u, clock, s)) {
    
    
						return false;
					}
					break;
				case VertexStatus::discovered:
					type(v, u) = EdgeStatus::backward;
#if DSA_MODE
					printf("edge(%d, %d) ", v, u); print(vertex(v)); printf(" ->"); print(vertex(u)); printf(" now is "); print(type(v, u)); printf("\n");
#endif

					return false;
					break;
				default: // visited
					type(v, u) = (dTime(v) < dTime(u)) ? EdgeStatus::forward : EdgeStatus::cross;
#if DSA_MODE
					printf("edge(%d, %d) ", v, u); print(vertex(v)); printf(" ->"); print(vertex(u)); printf(" now is "); print(type(v, u)); printf("\n");
#endif

					break;
			}


		}

		status(v) = VertexStatus::visited;
		s->push(vertex(v));

#if DSA_MODE
		printf("vetex v=%d", v); print(vertex(v)); printf(" status changed to VertexStatus::visited\n");
#endif

		return true;
	}

public:
	//! 顶点总数
	int n;
	//! 插入顶点,返回编号
	virtual int insert(Tv const& v) = 0;
	//! 删除顶点及其关联边,返回该顶点
	virtual Tv remove(int v) = 0;
	//! 顶点数据(该顶点的确存在)
	virtual Tv& vertex(int v) = 0;
	//! 顶点的入度(该顶点的确存在)
	virtual int inDegree(int v) = 0;
	//! 顶点的出度(该顶点的确存在)
	virtual int outDegree(int v) = 0;
	//! 顶点的首个邻接顶点
	virtual int firstNbr(int v) = 0;
	//! 顶点v的(相对于顶点j的)下一个邻接顶点
	virtual int nextNbr(int v, int j) = 0;
	//! 顶点状态
	virtual VertexStatus& status(int v) = 0;
	//! 顶点 discovered time
	virtual int& dTime(int v) = 0;
	//! 顶点 finished time
	virtual int& fTime(int v) = 0;
	//! 顶点在遍历树中的父亲
	virtual int& parent(int v) = 0;
	//! 顶点在遍历树中的优先级
	virtual int& priority(int v) = 0;

	//! 边总数
	int e;
	//! 边(v,u)是否存在
	virtual bool exists(int v, int u) = 0;
	//! 在顶点v、u之间插入权重为w的边e
	virtual void insert(Te const& e, int w, int v , int u) = 0;
	//! 的喊出顶点v、u之间的边e,返回该边信息
	virtual Te remove(int v, int u) = 0;
	//! 边(v,u)的数据(该边的确存在)
	virtual EdgeStatus& type(int v, int u) = 0;
	//! 边(v,u)的权重
	virtual int& weight(int v, int u) = 0;

	//********* algorithms *********************//

	//! 广度优先搜索
	void bfs(int s) {
    
    
		reset();
		int clock = 0;
		int v = s;
		do {
    
    
			if (VertexStatus::undiscovered == status(v)) {
    
    
				BFS(v, clock);
			}
		} while (s != (v = (++v % n)));
	}

	//! 深度优先搜索
	void dfs(int s) {
    
    
		reset();
		int clock = 0;
		int v = s;
		do {
    
    
			if (VertexStatus::undiscovered == status(v)) {
    
    
				DFS(v, clock);
			}
		} while (s != (v = (++v % n)));
	}
	//! 基于DFS的拓扑排序算法
	Stack<Tv>* tsort(int s) {
    
    
		assert(0 <= s && s < n);
		reset();
		int clock = 0, v = s;
		auto S = new Stack<Tv>();
		do {
    
    
			if (status(v) == VertexStatus::undiscovered) {
    
    
				if (!TSORT(v, clock, S)) {
    
    
					S->clear();
					break;
				}
			}
		} while (s != (v = (++v % n)));
		return S;
	}
};
}

6.1.4graphMatrix类

#pragma once

#include "graph.h"
#include "vector.h"
#include <assert.h>

namespace dtl 
{
    
    

//! 顶点
template <typename T>
struct Vertex
{
    
    
	T data;
	int inDegree;//入度
	int outDegree;//出度
	VertexStatus status;//定点状态: 未发现undiscovered// 已发现discovered,// 已访问visited
	//! discovered time
	int dTime;
	//! finished visit time
	int fTime;
	int parent;
	int priority;//优先级

	Vertex(T const& data = T(0))
		: data(data)
		, inDegree(0)
		, outDegree(0)
		, status(VertexStatus::undiscovered)
		, dTime(-1)
		, fTime(-1)
		, parent(-1)
		, priority(INT_MAX)
	{
    
    }
};

//! 边
template <typename T>
struct Edge
{
    
    
	T data;//数据
	int weight;//权重
	EdgeStatus status;//状态

	Edge(T const& data, int weight)
		: data(data)
		, weight(weight)
		, status(EdgeStatus::undetermined)
	{
    
    }
};

//邻接矩阵
template <typename Tv, typename Te>
class GraphMatrix : public Graph<Tv, Te>
{
    
    
private:
	//! vertexes
	Vector<Vertex<Tv>> V;//顶点
	//! edges
	Vector<Vector<Edge<Te>*>> E;//边

public:
	typedef Graph<Tv, Te> GraphType;

	GraphMatrix() {
    
    
		GraphType::n = GraphType::e = 0;
	}

	~GraphMatrix() {
    
    
		for (int j = 0; j < GraphType::n; j++) {
    
    
			for (int k = 0; k < GraphType::n; k++) {
    
    
				delete E[j][k];
			}
		}
	}

	// vertex basic opration
	virtual Tv& vertex(int i) {
    
     return V[i].data; }
	virtual int inDegree(int i) {
    
     return V[i].inDegree; }
	virtual int outDegree(int i) {
    
     return V[i].outDegree; }
	virtual VertexStatus& status(int i) {
    
     return V[i].status; }
	virtual int& dTime(int i) {
    
     return V[i].dTime; }
	virtual int& fTime(int i) {
    
     return V[i].fTime; }
	virtual int& parent(int i) {
    
     return V[i].parent; }
	virtual int& priority(int i) {
    
     return V[i].priority; }

	virtual int firstNbr(int i) {
    
     return nextNbr(i, GraphType::n); }
	virtual int nextNbr(int i, int j) {
    
    
		while ((-1 < j) && (!exists(i, --j))) {
    
    }
		return j;
	}

	// vertex dynamic operation
	virtual int insert(Tv const& vertex) {
    
    
		for (int j = 0; j < GraphType::n; j++) {
    
    
			E[j].insert(nullptr);
		}
		GraphType::n++;
		E.insert(Vector<Edge<Te>*>(GraphType::n, GraphType::n, (Edge<Te>*)nullptr));
		return V.insert(Vertex<Tv>(vertex));
	}

	virtual Tv remove(int i) {
    
    
		assert(0 <= i && i < GraphType::n);
		for (int j = 0; j < GraphType::n; j++) {
    
    
			if (exists(i, j)) {
    
    
				delete E[i][j];
				V[j].inDegree--;
			}
		}
		E.remove(i);
		GraphType::n--;
		Tv vBak = vertex(i);
		V.remove(i);
		for (int j = 0; j < GraphType::n; j++) {
    
    
			if (Edge<Te>* e = E[j].remove(i)) {
    
    
				delete e;
				V[j].outDegree--;
			}
		}
		return vBak;
	}

	// edge operation
	virtual bool exists(int i, int j) {
    
    
		return (0 <= i) && (i < GraphType::n) && (0 <= j) && (j < GraphType::n) && E[i][j] != nullptr;
	}

	virtual EdgeStatus& type(int i, int j) {
    
     assert(exists(i, j)); return E[i][j]->status; }
	virtual Te& edge(int i, int j) {
    
     assert(exists(i, j)); return E[i][j]->data; }
	virtual int& weight(int i, int j) {
    
     assert(exists(i, j)); return E[i][j]->weight; }

	virtual void insert(Te const& edge, int weight, int i, int j) {
    
    
		if (exists(i, j)) {
    
     return; }
		E[i][j] = new Edge<Te>(edge, weight);
		GraphType::e++;
		V[i].outDegree++;
		V[j].inDegree++;
	}

	virtual Te remove(int i, int j) {
    
    
		assert(exists(i, j));
		Te eBak = edge(i, j);
		delete E[i][j];
		E[i][j] = nullptr;
		GraphType::e--;
		V[i].outDegree--;
		V[j].inDegree--;
		return eBak;
	}
};

}

猜你喜欢

转载自blog.csdn.net/ZXG20000/article/details/113855403
6.1