图的操作 code

图的邻接表和邻接矩阵表示

图的算法:深度优先遍历、广度优先遍历、拓扑排序、Dijkstra算法(单源最短路径)、floyd算法(每对顶点之间的最短距离)

     Prim算法(最小生成树)、Kruskal算法(最小生成树)


代码根据网上资源整理规范补充而完成,感谢先行者

MHeap实现见前篇  heap 实现 code


/*
 * 图的表示及相关算法的实现
 
 * class类:
 * GraphMatrix 邻接矩阵形式
 * GraphList 邻接表形式
 *
 * api接口:
 * GraphTraverse 图的遍历
 * NonPreFirstTopsort 无前趋的顶点优先的拓扑排序
 * DfsTopsort 有向无环图深度优先遍历拓扑排序
 * Dijkstra Dijkstra算法
 * Floyd floyd算法
 * Prim Prim算法
 * KruskalKruskal算法
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <list>
#include <stack>
#include <queue>
#include "heap"

using namespace std;

enum {
	UNVISITED,
	VISITED
};

#define INFINITY 100000000


/**************** 图的边的定义 ***************/
struct Edge
{
	// from是边的始点, to是边的终点, weight是边的权值
	int from, to, weight;
	
	
	Edge()                 //构造函数
	{
		from = -1;   
		to = -1;  
		weight = 0;
	}
	
	Edge(int f,int t,int w)  //构造函数
	{
		from = f;   
		to = t;   
		weight = w;
	}

	
	bool operator >=(const Edge e) const
	{
		return weight > e.weight;
	}
	
	bool operator <=(const Edge e) const
	{
		return weight < e.weight;
	}
};

/**************** 图的定义 ***************/
class Graph
{
public:	
	Graph(int n)
	{
		NVertex = n;
		NEdge = 0;
		InDegree = new int[NVertex];
		Mark = new int[NVertex];
		for(int i = 0; i < NVertex; i++)
		{
			Mark[i] = UNVISITED;
			InDegree[i] = 0;
		}
	}
	
	~Graph()                         // 析构函数
	{
		delete [] Mark;
		delete [] InDegree;
	}
	
	int GetVertexNum()
	{
		return NVertex;
	}
	
	int GetEdgeNum()
	{
		return NEdge;
	}
	
	bool IsEdge(const Edge e)
	{
		return e.to >= 0 && e.weight > 0 && e.weight < INFINITY;
	}
	
	int GetFromVertex(Edge e)
	{
		return e.from;
	}
	
	int GetToVertex(Edge e)
	{
		return e.to;
	}
	
	int GetWeight(Edge e)
	{
		return e.weight;
	}
	
	int *GetMark()
	{
		return Mark;
	}
	
	void ClearMark() 
	{
		for (int i = 0; i < NVertex; i++)
			Mark[i] = UNVISITED;
	}
	
	int *GetInDegree() 
	{
		return InDegree;
	}
	
	// 返回与顶点v相关联的第一条边
	virtual const Edge &GetFirstEdge(int v) = 0;
	
	// 返回与边e有相同关联顶点e的下一条边
	virtual const Edge &GetNextEdge(const Edge &e) = 0;
	
	virtual void SetEdge(int from, int to, int weight) = 0;
	virtual void DelEdge(int from, int to) = 0;
	
protected:
	int NVertex;       // 最多可以保存的图的顶点的个数
	int NEdge;     	   // 图的边的个数
	int *Mark;         // Mark标记顶点是否访问过
	int *InDegree;     // Indegree保存顶点的入度
};


/**************** 邻接矩阵表示的图 ***************/
class GraphMatrix: public Graph   
{
private:
	int **GMatrix;           // 指向相邻矩阵的指针
 
public:

	// n是图的顶点数
	GraphMatrix(int n): Graph(n)
	{
		GMatrix = (int**)new int*[NVertex];
		
		for(int i = 0; i < NVertex; i++)
			GMatrix[i]  =  new int[NVertex];

		for(int i = 0; i < NVertex; i++) {              
			for(int j = 0; j < NVertex; j++)
				GMatrix[i][j] = 0;
		}
	}
	
	~GraphMatrix()
	{
		for(int i = 0; i < NVertex; i++)
			delete [] GMatrix[i];
		delete [] GMatrix;
	}
	
	const Edge &GetFirstEdge(int v)
	{
		static Edge e;
		e.from = v;
		e.to = -1;
		e.weight = 0;
		
		for(int i = 0;i < NVertex; i++) {
			if(GMatrix[v][i] != 0) {
				e.to = i;
				e.weight = GMatrix[v][i];
				break;
			}   
		}
		
		return e;
	}
	
	const Edge &GetNextEdge(const Edge &e)
	{
		static Edge e1;
		
		e1.from = e.from;
		e1.to = -1;
		e1.weight = 0;
		
		for(int i = e.to + 1; i < NVertex; i++) {
			if(GMatrix[e.from][i] != 0) {
				e1.to = i;
				e1.weight = GMatrix[e.from][i];
				break;
			}
		}
		
		return e1;
	}
	
	void SetEdge(int from, int to, int weight)
	{
		if (weight <= 0 || weight >= INFINITY)
			weight = 0;
		else {
			if (GMatrix[from][to] == 0) {
				NEdge++;
				InDegree[to]++;
			}
		}			
		
		GMatrix[from][to] = weight;          
	}
	
	void DelEdge(int from, int to)
	{
		if(GMatrix[from][to] != 0) {		
			NEdge--;
			InDegree[to]--;
		}
		GMatrix[from][to] = 0;       
	}
};


/**************** 邻接表表示的图 ***************/
struct ListNode      // 邻接表节点定义
{
	int vertex;      // 边的终点
	int weight;      // 边的权
	
	ListNode(int v, int w)
	{
		vertex = v;
		weight = w;
	}
	
	bool operator ==(const ListNode ln)
	{
		return vertex == ln.vertex;
	}
};


class GraphList: public Graph                    
{
public:
	GraphList(int n): Graph(n)
	{
		GList = new list<ListNode> *[NVertex];
		for (int i = 0; i < NVertex; i++) {
			GList[i] = new list<ListNode>;
		}
	}
	
	~GraphList()
	{
		for (int i = 0; i < NVertex; i++) {
			delete GList[i];
		}
		delete [] GList;
	}
	
	const Edge &GetFirstEdge(int v)              //返回顶点oneVertex的第一条边
	{
		static Edge e;
		list<ListNode> *temp = GList[v];
		list<ListNode>::iterator it = temp->begin();
		
		e.from = v;
		e.to = -1;
		e.weight = 0;
		
		if(it != temp->end())
		{
			e.to = it->vertex;
			e.weight = it->weight;
		}
		
		return e;
	}
	
	const Edge &GetNextEdge(const Edge &e)
	{
		static Edge e1;
		list<ListNode> *temp = GList[e.from];           
		list<ListNode>::iterator it = temp->begin();
		
		e1.from = e.from;
		e1.to = -1;
		e1.weight = 0;
		
		while (it != temp->end() && it->vertex <= e.to)
			it++;
			
		if (it != temp->end()) {
			e1.to = it->vertex;
			e1.weight = it->weight;   
		}
		
		return e1;
	}
	
	void SetEdge(int from, int to, int weight)   // 为图设定一条边
	{
		ListNode node(to, weight);
		list<ListNode> *temp = GList[from];
		list<ListNode>::iterator it = temp->begin();
		
		while(it != temp->end() && it->vertex < to)
			it++;
		
		if (it != temp->end() && it->vertex == to) {
			it->weight = weight;
			return;
		}
		
		temp->insert(it, node);
		NEdge++;
		InDegree[to]++;
		
		return;
	}
		
	void DelEdge(int from,int to)
	{
		list<ListNode> *temp = GList[from];
		list<ListNode>::iterator it = temp->begin();
		
		while(it != temp->end() && it->vertex < to)
			it++;
		
		if (it != temp->end() && it->vertex == to) {
			temp->erase(it);
			NEdge--;
			InDegree[to]--;
		}
		
	}

private:
	list<ListNode> **GList;
 
};




//typedef void (*CallbackFunc)(void *data);

/************** 图相关算法部分 *************/

// 深度优先遍历:
void DFS(Graph &g, int v)
{
	int *mark = g.GetMark();
	mark[v] = VISITED;	
	printf("%d ", v);
	
	for(Edge e = g.GetFirstEdge(v); g.IsEdge(e); e = g.GetNextEdge(e)) {
		int vertex = g.GetToVertex(e);
		if(vertex >= 0 && mark[vertex] == UNVISITED) {
			DFS(g, vertex); 
		}
	}
		
	return;
}

// 广度优先遍历
void BFS(Graph& g, int v)
{
	queue<int> q;
	int *mark = g.GetMark();
	
	mark[v]= VISITED;
	q.push(v);
	printf("%d ", v);
	
	while(!q.empty()) {
		int vertex = q.front();
		q.pop();
		
		for(Edge e = g.GetFirstEdge(vertex); g.IsEdge(e); e = g.GetNextEdge(e)) { 
			int vertex2 = g.GetToVertex(e);
			if(vertex2 >= 0 && mark[vertex2]== UNVISITED) {    
				mark[vertex2]= VISITED;
				printf("%d ", vertex2);
				q.push(vertex2); 
			}   
		}
	}
	
	return;
}

// 图的遍历
void GraphTraverse(Graph& g, bool dfs)
{
	int *mark = g.GetMark();
	int nvertex = g.GetVertexNum();
	g.ClearMark();
	
	for(int i = 0; i < nvertex; i++) {
		if(mark[i]== UNVISITED) {
			if(dfs)
				DFS(g, i);       //深度优先搜索
			else
				BFS(g, i);       //广度优先搜索
		}
	}
	
	return;
}

// 无前趋的顶点优先的拓扑排序
void NonPreFirstTopsort(Graph& g)                     
{
	queue<int> q;
	int nvertex = g.GetVertexNum();
	int *mark = g.GetMark();
	int *indegree = new int[nvertex];
	
	memcpy(indegree, g.GetInDegree(), sizeof(int) * nvertex);
	g.ClearMark();
	
	// 图中无前驱的顶点入队
	for(int i = 0; i < nvertex; i++) {
		if(indegree[i] == 0)
			q.push(i);                            
	}
	
	while(!q.empty()) {
		int v = q.front();
		q.pop();
		printf("%d ", v);
		mark[v] = VISITED;
		
		for(Edge e = g.GetFirstEdge(v); g.IsEdge(e); e = g.GetNextEdge(e)) {
			int vertex = g.GetToVertex(e);
			indegree[vertex]--;
			
			// 入度为零说明前面的顶点都已经处理,接下来可以处理此节点
			if(indegree[vertex] == 0)
				q.push(vertex);
		}
	}
	
	for(int i = 0; i < nvertex; i++) { 
		if(mark[i] == UNVISITED) {            
			printf("ERROR:graph is circled\n");        //图有环
			break;
		}
	} 
	
	delete [] indegree;
	return;
}


void DoTopsort(Graph& g, int v,int *result,int &tag)
{
	int *mark = g.GetMark();
	
	mark[v]= VISITED;
	for(Edge e =  g.GetFirstEdge(v); g.IsEdge(e); e = g.GetNextEdge(e)) {  
		if(mark[g.GetToVertex(e)] == UNVISITED)
			DoTopsort(g, g.GetToVertex(e),result,tag);
	}
	result[tag++] = v;
	
	return;
}

// 有向无环图(Directed Acyclic Graph)深度优先遍历拓扑排序
void DfsTopsort(Graph& g)
{
	int nvertex = g.GetVertexNum();
	int *result = new int[nvertex];
	int *mark = g.GetMark();
	int tag = 0;
	
	g.ClearMark();
	for(int i = 0; i < nvertex; i++) {
		if(mark[i] == UNVISITED)
			DoTopsort(g,i,result,tag);
	}
	
	for(int i = nvertex - 1; i >= 0; i--)  
		printf("%d ", result[i]);
	
	delete [] result;
	return;
}

// 用于minheap堆,方便快速取得最小的dist
struct DijDist
{
	int vertex;
	int distance;
	
	DijDist(int v = -1, int d = INFINITY)
	{
		vertex = v;
		distance = d;
	} 
	
	bool operator <=(const DijDist &elem) const
	{
		return distance <= elem.distance;
	}
	
	bool operator >=(const DijDist &elem) const
	{
		return distance >= elem.distance;
	}
};


// Dijkstra算法(单源最短路径),dist保存src到各个点的距离,path
// 中保存src到各个点的最短路径上的点, dist、path都无需外部初始化
// path[i]是src到i的最短路径上紧邻i的前面的点
void Dijkstra(Graph& g, int src, int *dist, int *path)
{
	int nvertex = g.GetVertexNum();
	int *mark = g.GetMark();
	MHeap<DijDist> minheap(nvertex, 0);
	DijDist de;
	
	de.vertex = src;
	de.distance = 0;
	minheap.Push(de);
	
	for (int i = 0; i < nvertex; i++) {
		dist[i] = INFINITY;
		path[i] = -1;
	}
	dist[src] = 0;
	
	g.ClearMark();
	for(int i = 0; i < nvertex; i++) {
		int v = -1;
		
		do {
			if(minheap.Empty())
				break;
			minheap.Top(de);
			minheap.Pop();
			v = de.vertex;
		} while(mark[v] == VISITED);
	
		if(v == -1 || mark[v] == VISITED)                          
			break;
			
		mark[v] = VISITED;
		
		for(Edge e = g.GetFirstEdge(v); g.IsEdge(e); e = g.GetNextEdge(e)) {
			int vertex = g.GetToVertex(e);
			if(dist[vertex] > (dist[v] + g.GetWeight(e)))
			{
				dist[vertex] = dist[v] + g.GetWeight(e);                 
				de.vertex = vertex;
				de.distance = dist[vertex];
				minheap.Push(de);
				path[vertex] = v;
			}
		}
	} 
}

// 打印src到target最短路径上的各个点 
void DijShowPath(int *path, int src, int target)   
{
    stack<int> st;
    while(target != src) {
		st.push(target);
        target = path[target];
		
		// src到target没有到达路径
		if (target == -1) {
			st.push(-1);
			break;
		}
		
    }
	st.push(src);
	
    while(st.size() > 1) {
        printf("%d -> ", st.top());
        st.pop();
    }
	
	printf("%d\n", st.top());
} 

// floyd算法,计算每对顶点之间的最短距离
void Floyd(Graph& g,int **dist, int **path)
{
	int nvertex = g.GetVertexNum();
	
	// dist数组, 初始化每对顶点之间的距离
	for(int i = 0;i < nvertex; i++) {
		for(int j = 0; j < nvertex; j++) {
			dist[i][j] = i == j ? 0 : INFINITY;
			path[i][j] = -1;
		}
		
		for(Edge e = g.GetFirstEdge(i); g.IsEdge(e); e = g.GetNextEdge(e)) {
			dist[i][e.to] = g.GetWeight(e);
			path[i][e.to] = i;
		}
	}

	
	// 若j、k两个顶点间的最短路径包含顶点i,则有dist[j][k] > (dist[j][i] + dist[i][k])
	// 通过i测试到所有的点,从而更新每对顶点之间距离
	for(int i = 0; i < nvertex; i++) {
		for(int j = 0; j < nvertex; j++) {
			for(int k = 0; k < nvertex; k++) {
				if(dist[j][k] > (dist[j][i] + dist[i][k])) {
					dist[j][k] = dist[j][i] + dist[i][k];
					path[j][k] = i;
				}
			}
		}
	}
	
	for(int i = 0; i < nvertex; i++) {
		for(int j = 0;j < nvertex; j++)
			printf("dist<%d, %d>: %d\n", i, j, dist[i][j]);
	}
	
	for(int i = 0; i < nvertex; i++) {
		for(int j = 0;j < nvertex; j++)
			printf("path<%d, %d>: %d\n", i, j, path[i][j]);
	}
}

// 递归打印路径
void FloydShowPath_(int **path, int src, int target) 
{
	if (src == target || path[src][target] == src)
		return;
		
	if (path[src][target] == -1) {	
		printf("-1 -> ");
		return;
	}

	FloydShowPath_(path, src, path[src][target]);
	printf("%d -> ", path[src][target]);
	FloydShowPath_(path, path[src][target], target);
}

// 打印src到target最短路径上的各个点
void FloydShowPath(int **path, int src, int target) 
{
	if (src == target) {
		printf("%d\n", src);
		return;
	}
	
	printf("%d -> ", src);
	FloydShowPath_(path, src, target);
	printf("%d\n", target);
}


// Prim算法, 最小生成树(Minimum Spanning Tree)
// 用于无向图、稠密图(边很多)
void Prim(Graph& g, int src)        
{ 
	int nvertex = g.GetVertexNum();
	int *mark = g.GetMark();
	int nedge = g.GetEdgeNum();
	MHeap<Edge> minheap(nedge, 0);
	Edge *mst = new Edge[nedge];
	int nmst = 0;
	
	if (nedge < nvertex - 1) {
		printf("Error: NO MST\n");
		return;
	}
	
	g.ClearMark();
	mark[src] = VISITED;

	for(Edge e = g.GetFirstEdge(src); g.IsEdge(e); e = g.GetNextEdge(e))
		minheap.Push(e);
	
	for(int i = 1; i < nvertex; i++) {
		Edge temp;
		int v = -1;
		
		// 寻找权值最小的边
		do {
			if(minheap.Empty())
				break;
			minheap.Top(temp);
			minheap.Pop();
			v = temp.to;
		} while(mark[v] == VISITED);
		
		if (v == -1 || mark[v] == VISITED)
			break;

		// 添加到mst中
		mst[nmst++] = temp;
		mark[temp.to] = VISITED;
		for(Edge e = g.GetFirstEdge(temp.to); g.IsEdge(e); e = g.GetNextEdge(e)) {
			if(mark[e.to] == VISITED)
				continue;    
			minheap.Push(e);
		}
	} 
	
	if (nmst < nvertex - 1) {
		printf("Error: NO MST\n");
		return;
	}
		
	for (int i = 0; i < nmst; i++) {
		printf("%d -> %d: %d\n", mst[i].from, mst[i].to, mst[i].weight);
	}
}

// 并查集用于Kruskal算法
class UnionFind
{
private:
	int *ancestor;             
	int size;
	
public:
	UnionFind(int n)
	{
		size = n;
		ancestor = new int[n];
		for(int i = 0; i < n; i++) {
			// 每一个元素的祖先节点是它本身
			ancestor[i] = i;  
		}
	}
	~UnionFind()
	{
		delete [] ancestor;
	}
	
	// 寻找祖先节点
	int Find(int a)
	{
		if(a != ancestor[a]) { 
			//回溯时的压缩路径  
			ancestor[a] = Find(ancestor[a]);
		}
		
		return ancestor[a];  
	}
	
	// 合并a和b所在的集合
	void Union(int a,int b)     
	{
		int root1 = Find(a);
		int root2 = Find(b);
		if(root1 < root2) 
			ancestor[root2] = root1;
		else if (root1 > root2)
			ancestor[root1] = root2;
	}
	
	// a和b是否在同一集合中
	bool Differ(int a,int b)    
	{
		int root1 = Find(a);
		int root2 = Find(b);
		return root1 != root2;
	}
};

// Kruskal算法,最小生成树(Minimum Spanning Tree)
// 通过小堆、并查集实现
void Kruskal(Graph& g)       
{
	int nvertex = g.GetVertexNum();
	int nedge = g.GetEdgeNum();
	MHeap<Edge> minheap(nedge, 0);
	Edge *mst = new Edge[nedge];
	UnionFind uf(nvertex);
	int nmst = 0;
	
	if (nedge < nvertex - 1) {
		printf("Error: NO MST\n");
		return;
	}
	

	
	// 图的所有边push入堆
	for(int i = 0; i < nvertex; i++) {
		for(Edge e =  g.GetFirstEdge(i); g.IsEdge(e); e = g.GetNextEdge(e)) {   
			// 无向图可以加此条件判断,减少冗余边处理
			// if(g.GetFromVertex(e) < g.GetToVertex(e))
			minheap.Push(e);  
		}
	}
	
	for(int i = 1; i < nvertex; ) {
		Edge e;
		int from;
		int to;
		
		// 获得下一条权值最小的边
		minheap.Top(e);
		minheap.Pop();
		
		from = g.GetFromVertex(e);   //记录该条边的信息
		to =  g.GetToVertex(e);
		
		if(uf.Differ(from,to))       //如果边e的两个顶点不在一个等价类
		{
			uf.Union(from,to);       //将边e的两个顶点所在的两个等价类合并为一个
			mst[nmst++] = e;
			i++;
		}
	}
	
		
	if (nmst < nvertex - 1) {
		printf("Error: NO MST\n");
		return;
	}
		
	for (int i = 0; i < nmst; i++) {
		printf("%d -> %d: %d\n", mst[i].from, mst[i].to, mst[i].weight);
	}
}


/* 输入形式:
 * line1:图的表示形式(1 邻接矩阵,0 邻接表)
 * line2:图是否有向图(1 有向图, 0 无向图)
 * line3:图的顶点数
 * line4:图的边数
 * line5 ~ end :图的边(from to weight 空格隔开)
 */
int main()
{
	int gform;					// 图的表示形式
	int directed;				// 图是否有向图
	int nvertex;				// 图的顶点数
	int nedge;					// 图的边数

	Graph *g;
	
	scanf("%d", &gform);
	scanf("%d", &directed);
	scanf("%d", &nvertex);
	scanf("%d", &nedge);
	                        
	if(gform == 0)
		g = new GraphList(nvertex);
	else
		g = new GraphMatrix(nvertex);
 
	while (nedge-- > 0) {
		int from, to, weight;       // 每条边的起点、终点和权值
		
		scanf("%d %d %d", &from, &to, &weight);
		g->SetEdge(from, to, weight);
		if (!directed)
			g->SetEdge(to, from, weight);
	}
	
	printf("\n\nDFS:\n");
	GraphTraverse(*g, true);
	printf("\n\n");
	
	printf("BFS:\n");
	GraphTraverse(*g, false);
	printf("\n\n");
	
	printf("NonPreFirstTopsort:\n");
	NonPreFirstTopsort(*g);
	printf("\n\n");
	
	if (directed) {
		printf("DfsTopsort:\n");
		DfsTopsort(*g);
		printf("\n\n");
	}
	
	printf("DijShowPath:\n");
	int *dist = new int[nvertex];
	int *path = new int[nvertex];
	Dijkstra(*g, nvertex / 2, dist, path);
	DijShowPath(path, nvertex / 2, nvertex - 1);
	delete [] dist;
	delete [] path;
	printf("\n");
	
	printf("FloydShowPath:\n");
	int **distt = new int *[nvertex];
	int **pathh = new int *[nvertex];
	for (int i = 0; i < nvertex; i++) {
		distt[i] = new int[nvertex];
		pathh[i] = new int[nvertex];
	}
	Floyd(*g, distt, pathh);
	FloydShowPath(pathh, nvertex / 2, nvertex - 1);
	for (int i = 0; i < nvertex; i++) {
		delete [] distt[i];
		delete [] pathh[i];
	}
	delete [] distt;
	delete [] pathh;
	printf("\n");
	
	printf("Prim:\n");
	Prim(*g, nvertex / 2);
	printf("\n");
	
	printf("Kruskal:\n");
	Kruskal(*g);
	
	
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值