邻接表存储的图模板以及相关算法实现(C++、多文件)

tips:参考数据结构思想与实现书中的思想,编写和测试了邻接表存储的图模板以及相关算法的程序。

注意事项:

在C++中,模版和普通的函数或类性质差异较大。编译器在实例化一个类时,需要知道该类的所有信息。普通的类的信息通过头文件中的声明式可以完全确定。但是对于模版类,如果将头文件和源文件分开,编译器在编译头文件时只存放模板类的符号,不能找到其源文件位置也就不能编译实现它的源文件。因此在最后链接时发现没有模版类的实现,就会报错。

Solution:

将类模板的声明式和定义式放在同一个文件中,为了与头文件进行区分,命名为.hpp文件。

/**
* 图的抽象类 -- 可以让不同存储方式的图继承它
* graph.h
*/
#pragma once
#ifndef GRAPH
#define GRAPH
#include <iostream>
using namespace std;

/*图的抽象类*/
template <class TypeOfVer, class TypeOfEdge>
class graph {
protected:
	int Vers;
	int Edges;

public:
	virtual void insert(TypeOfVer x, TypeOfVer y, TypeOfEdge w) = 0;
	virtual void remove(TypeOfVer x, TypeOfVer y) = 0;
	virtual bool exist(TypeOfVer x, TypeOfVer y) const = 0; //判断两个顶点是否存在边
	int numOfVer() const { return this->Vers; }
	int numOfEdge() const { return this->Edges; }
};

#endif
/**
*邻接表存储的图模板的实现
*adjListGraph.hpp
*/

#pragma once
/*
 * @Descripttion:
 * @version:
 * @Author: faker
 * @Date: 2022-10-07 15:52:49
 * @LastEditors: Please set LastEditors
 * @LastEditTime: 2022-10-08 09:53:46
 */

#ifndef ADJLISTGRAPH

#define ADJLISTGRAPH
#include <iostream>
#include "graph.h"
#include "disjointedSet.h"
#include <queue>
using namespace std;

template <class TypeOfVer, class TypeOfEdge>
class adjListGraph : public graph<TypeOfVer, TypeOfEdge> {
public:
	adjListGraph(int vSize, const TypeOfVer d[]);
	virtual void insert(TypeOfVer x, TypeOfVer y, TypeOfEdge w);
	virtual void remove(TypeOfVer x, TypeOfVer y);
	virtual bool exist(TypeOfVer x, TypeOfVer y) const;
	~adjListGraph();

	void bfs() const;
	void dfs() const;
	void dfs(int i, bool visited[]) const;
	//void floyd() const;  邻接矩阵存储的图
	void dijstra(TypeOfVer start, TypeOfEdge noEdge) const;
	void printPath(int startNum, int endNum, int* prev) const;


	void prim(TypeOfEdge noEdge) const;  //选顶点--子图时刻连通
	void kruskal() const;				 //选边

	void topSort() const;
	void criticalPath() const;

private:
	struct edgeNode {
		int end;  //终点编号
		TypeOfEdge weight;  //边的权值
		edgeNode* next;

		edgeNode(int e, TypeOfEdge w, edgeNode* n = NULL) {
			end = e; weight = w; next = n;
		}
	};

	struct verNode {
		TypeOfVer ver;  //顶点值
		edgeNode* head; //对应单链表的头指针

		verNode(edgeNode* h = NULL) { head = h; }
	};

	/*仅在kruskal算法中使用*/
	struct edge {
		int beg, end;
		TypeOfEdge w;
		//将小的往上放
		bool operator<(const edge& rp) const { return w > rp.w; }
	};


	verNode* verList;

	int find(TypeOfVer v)const {
		for (int i = 0; i < this->Vers; ++i) {
			if (verList[i].ver == v) return i;
		}
	}
};






template <class TypeOfVer, class TypeOfEdge>
void adjListGraph<TypeOfVer, TypeOfEdge>::printPath(int startNum, int endNum, int* prev) const {
	/*打印第一个点*/
	if (startNum == endNum) {
		cout << verList[startNum].ver;
		return;
	}
	//回溯
	//加判断?
	if (endNum > this->Vers || endNum < 0) return;
	printPath(startNum, prev[endNum], prev);
	cout << '-' << verList[endNum].ver;
}

template<class TypeOfVer, class TypeOfEdge>
void adjListGraph<TypeOfVer, TypeOfEdge>::prim(TypeOfEdge noEdge) const {
	bool* flag = new bool[this->Vers];
	TypeOfEdge* lowCost = new TypeOfEdge[this->Vers];
	int* startNode = new int[this->Vers];

	//初始化--刚开始所有结点都不在生成树中
	for (int i = 0; i < this->Vers; ++i) {
		flag[i] = false;
		lowCost[i] = noEdge;
	}

	int start = 0;  //将0结点作为开始结点放入生成树
	for (int i = 1; i < this->Vers; ++i) {
		for (edgeNode* p = verList[start].head; p != NULL; p = p->next) {
			//更新距离信息
			if (!flag[p->end] && lowCost[p->end] > p->weight) {
				lowCost[p->end] = p->weight;
				startNode[p->end] = start;
			}
		}

		flag[start] = true;
		int min = noEdge;
		for (int j = 0; j < this->Vers; ++j) {
			//寻找U到V-U的最小权值边
			if (lowCost[j] < min) {
				min = lowCost[j];
				start = j;
			}
		}
		cout << '(' << verList[startNode[start]].ver << ',' << verList[start].ver << ")\t";
		lowCost[start] = noEdge;
	}


	delete[] flag;
	delete[] lowCost;
	delete[] startNode;
}

template<class TypeOfVer, class TypeOfEdge>
void adjListGraph<TypeOfVer, TypeOfEdge>::kruskal() const {
	disjointedSet ds(this->Vers);
	priority_queue<edge> pq;
	edge e;

	//生成优先级队列 -- 自动排序
	for (int i = 0; i < this->Vers; ++i) {
		for (edgeNode* p = verList[i].head; p != NULL; p = p->next) {
			if (i < p->end) {
				e.beg = i;
				e.end = p->end;
				e.w = p->weight;
				pq.push(e);
			}
		}
	}

	int edgesAccepted = 0;
	int u, v;
	//开始归并
	while (edgesAccepted < this->Vers - 1) {
		e = pq.top(); //取出权值最小的边
		pq.pop();
		u = ds.Find(e.beg); v = ds.Find(e.end);
		if (u != v) {
			/*加入(u,v)不会成环*/
			edgesAccepted++;
			ds.Union(u, v);
			cout << "(" << verList[e.beg].ver << ',' << verList[e.end].ver << ")\t";
		}
	}
}

template<class TypeOfVer, class TypeOfEdge>
void adjListGraph<TypeOfVer, TypeOfEdge>::topSort() const {
	queue<int> q;
	int* inDegree = new int[this->Vers];

	/*计算每个点的入度*/
	for (int i = 0; i < this->Vers; ++i) {
		inDegree[i] = 0;
		for (edgeNode* p = verList[i].head; p != NULL; p = p->next) {
			++inDegree[p->end];
			//不是++inDegree[i] 出度
		}
	}

	/*入度为0的点入队*/
	for (int i = 0; i < this->Vers; ++i) {
		if (inDegree[i] == 0) {
			q.push(i);
		}
	}

	int currentNode;
	cout << "拓扑排序序列为:" << endl;
	while (!q.empty()) {
		currentNode = q.front();
		q.pop();
		cout << verList[currentNode].ver << ' ';
		for (edgeNode* p = verList[currentNode].head; p != NULL; p = p->next) {
			if (--inDegree[p->end] == 0) {
				/*与currentNode相邻结点的入度减1,如果度为0,入队*/
				q.push(p->end);
			}
		}
	}
	cout << endl;
	delete[] inDegree;
}

template<class TypeOfVer, class TypeOfEdge>
void adjListGraph<TypeOfVer, TypeOfEdge>::criticalPath() const {
	TypeOfEdge* ee = new TypeOfEdge[this->Vers]; //记录最早发生时间
	TypeOfEdge* le = new TypeOfEdge[this->Vers]; //记录最迟发生时间
	int* top = new int[this->Vers];		//保存拓扑序列
	int* inDegree = new int[this->Vers];
	queue<int> q;

	//找出拓扑序列
	for (int i = 0; i < this->Vers; ++i) {
		inDegree[i] = 0;
		for (edgeNode* p = verList[i].head; p != NULL; p = p->next) {
			++inDegree[p->end];
		}
	}

	//将入度为0的结点入队
	for (int i = 0; i < this->Vers; ++i) {
		if (inDegree[i] == 0) {
			q.push(i);
		}
	}

	int i = 0;
	while (!q.empty()) {
		top[i] = q.front();
		q.pop();
		for (edgeNode* p = verList[top[i]].head; p != NULL; p = p->next) {
			if (--inDegree[p->end] == 0) {
				q.push(p->end);
			}
		}
		++i;  //为了记录top序列
	}

	//找出最早发生时间
	for (int i = 0; i < this->Vers; ++i) {
		ee[i] = 0;
	}
	for (int i = 0; i < this->Vers; ++i) {
		for (edgeNode* p = verList[top[i]].head; p != NULL; p = p->next) {
			if (ee[p->end] < ee[top[i]] + p->weight) {
				ee[p->end] = ee[top[i]] + p->weight;
			}
		}
	}

	//找出最晚发生时间
	for (int i = 0; i < this->Vers; ++i) {
		le[i] = ee[this->Vers - 1];
	}
	for (int i = this->Vers - 1; i >= 0; --i) {
		for (edgeNode* p = verList[top[i]].head; p != NULL; p = p->next) {
			if (le[p->end] - p->weight < le[top[i]]) {
				le[top[i]] = le[p->end] - p->weight;
			}
		}
	}

	//找出关键路径
	for (int i = 0; i < this->Vers; ++i) {
		if (le[top[i]] == ee[top[i]]) {
			cout << '(' << verList[top[i]].ver << ',' << ee[top[i]] << ")\t";
		}
	}
}

template <class TypeOfVer, class TypeOfEdge>
void  adjListGraph<TypeOfVer, TypeOfEdge>::dijstra(TypeOfVer start, TypeOfEdge noEdge) const {
	TypeOfEdge* distance = new TypeOfEdge[this->Vers];
	int* prev = new int[this->Vers];
	bool* known = new bool[this->Vers];

	//初始化
	for (int i = 0; i < this->Vers; ++i) {
		distance[i] = noEdge;
		known[i] = false;
	}

	int startNum = find(start);

	distance[startNum] = 0; //设置源点路径为0
	prev[startNum] = startNum;


	int u;
	for (int i = 1; i < this->Vers; ++i) {
		/*寻找具有最短路径的结点*/
		int min = noEdge;
		for (int j = 0; j < this->Vers; ++j) {
			if (!known[j] && distance[j] < min) {
				min = distance[j];
				u = j;
			}
			known[u] = true;
		}

		for (edgeNode* p = verList[u].head; p != NULL; p = p->next) {
			//更新u的临近结点
			if (!known[p->end] && distance[p->end] > min + p->weight) {
				distance[p->end] = min + p->weight;
				prev[p->end] = u;
			}
		}
	}

	for (int i = 0; i < this->Vers; ++i) {
		/*输出*/
		cout << "从" << start << "到" << verList[i].ver << "的路径为:" << endl;
		printPath(startNum, i, prev);
		/*如果没有路径,不用在输出长度*/
		if (distance[i] != noEdge)
			cout << "\t长度为:" << distance[i];
		cout << endl;
	}

	delete[]distance;
	delete[]prev;
	delete[]known;
}

/*
//采用邻接矩阵做比较好
template <class TypeOfVer, class TypeOfEdge>
void adjListGraph<TypeOfVer, TypeOfEdge>::floyd() const{
	TypeOfEdge** d = new TypeOfEdge*[Vers];
	int** prev = new int*[Vers];	//prev[i][j]表示从i到j的最短路径需要经过的前一个点

	//初始化
	for(int i = 0; i < Vers; ++i){
		d[i] = new TypeOfEdge[Vers];
		prev[i] = new int[Vers];
		for(int j = 0; j < Vers; ++j){
			d[i][j] = edges[i][j];
			prev[i][j] = (edges[i][j] == noEdge) ? -1 : i;
		}
	}

	//迭代过程
	for(int k = 0; k < Vers; ++k){
		for(int i = 0; i < Vers; ++i){
			for(int j = 0; j < Vers; ++j){
				if(d[j][k] + d[k][i] < d[i][j]){
					d[i][j] = d[j][k] + d[k][i];
					prev[i][j] = prev[k][j];
				}
			}
		}
	}

	//输出
	cout << "最短路径长度:" << endl;
	for(int i = 0; i < Vers; ++i){
		cout << endl;
		for(int j = 0; j < Vers; ++j)
			cout << d[i][j] << '\t';
	}

	cout << "最短路径:" << endl;
	for(int i = 0; i < Vers; ++i){
		cout << endl;
		for(int j = 0; j < Vers; ++j)
			cout << prev[i][j] << '\t';
	}
}
*/


template <class TypeOfVer, class TypeOfEdge>
adjListGraph<TypeOfVer, TypeOfEdge>::adjListGraph(int vSize, const TypeOfVer d[]) {
	this->Vers = vSize; this->Edges = 0;  //抽象类中继承

	verList = new verNode[vSize];
	for (int i = 0; i < this->Vers; ++i) {
		verList[i].ver = d[i];
	}
}


template <class TypeOfVer, class TypeOfEdge>
adjListGraph<TypeOfVer, TypeOfEdge>::~adjListGraph() {
	edgeNode* p;

	for (int i = 0; i < this->Vers; ++i) {
		while ((p = verList[i].head) != NULL) {
			/*释放第i个结点的单链表*/
			verList[i].head = p->next;
			delete p;
		}
	}

	delete[] verList;
}


template <class TypeOfVer, class TypeOfEdge>
void adjListGraph<TypeOfVer, TypeOfEdge>::insert(TypeOfVer x, TypeOfVer y, TypeOfEdge w) {
	int u = find(x), v = find(y);
	verList[u].head = new edgeNode(v, w, verList[u].head);
	++this->Edges;
}


template <class TypeOfVer, class TypeOfEdge>
void adjListGraph<TypeOfVer, TypeOfEdge>::remove(TypeOfVer x, TypeOfVer y) {
	int u = find(x), v = find(y);
	edgeNode* p = verList[u].head, * q;

	if (p == NULL) return;

	if (p->end == v) {
		/*头结点就是被删除的边*/
		verList[u].head = p->next;
		delete p;
		--this->Edges;
		return;
	}

	while (p->next != NULL && p->next->end != v) p = p->next;
	if (p->next != NULL) {
		q = p->next;   //记录要删除的结点
		p->next = q->next;
		delete p;
		--this->Edges;
	}

}





template <class TypeOfVer, class TypeOfEdge>
bool adjListGraph<TypeOfVer, TypeOfEdge>::exist(TypeOfVer x, TypeOfVer y) const {
	int u = find(x), v = find(y);
	edgeNode* p = verList[u].head;

	while (p != NULL && p->end != v) {
		p = p->next;
	}
	return p == NULL ? false : true;
}


template <class TypeOfVer, class TypeOfEdge>
void adjListGraph<TypeOfVer, TypeOfEdge>::bfs() const {
	bool* visited = new bool[this->Vers];
	int currentNode;

	queue<int> q;
	edgeNode* p;

	for (int i = 0; i < this->Vers; ++i) visited[i] = false;

	for (int i = 0; i < this->Vers; ++i) {
		if (visited[i]) continue;
		q.push(i);
		while (!q.empty()) {
			currentNode = q.front();
			q.pop();
			if (visited[currentNode]) continue;
			cout << verList[currentNode].ver << "->";
			visited[currentNode] = true;
			p = verList[currentNode].head;
			while (p != NULL) {
				if (!visited[p->end])q.push(p->end);
				p = p->next;
			}
		}
		cout << endl;
	}
}



template <class TypeOfVer, class TypeOfEdge>
void adjListGraph<TypeOfVer, TypeOfEdge>::dfs() const {
	bool* visited = new bool[this->Vers];

	for (int i = 0; i < this->Vers; ++i) {
		visited[i] = false;
	}

	for (int i = 0; i < this->Vers; ++i) {
		if (!visited[i]) {
			dfs(i, visited);
		}
	}
}

template <class TypeOfVer, class TypeOfEdge>
void adjListGraph<TypeOfVer, TypeOfEdge>::dfs(int i, bool visited[]) const {
	edgeNode* p = verList[i].head;

	if (visited[i]) return;
	cout << verList[i].ver << "->";
	visited[i] = true;

	while (p != NULL) {
		if (visited[p->end] == false) dfs(p->end, visited);
		p = p->next;
	}
}

#endif

/**
*并查集(UF)或者不相交集(disjointedSet)数据结构的声明式 -- 为了完成kruskal算法
*disjointedSet.h
*/

#pragma once

#ifndef DISJOINTEDSET
#define DISJOINTEDSET

class disjointedSet {
private:
	int* parent;
	int size;
public:
	disjointedSet(int s);
	int Find(int x);
	void Union(int root1, int root2);
	~disjointedSet() { delete[] parent; }
};

#endif
/**
*disjointedSet.cpp
*/
#include "disjointedSet.h"

disjointedSet::disjointedSet(int s) {
	size = s;
	parent = new int[size];
	for (int i = 0; i < size; ++i)
		parent[i] = -1;
}

//压缩路径
int disjointedSet::Find(int x) {
	if (parent[x] < 0) return x; //根节点
	return parent[x] = Find(parent[x]); //往上放,使高度降低
}

void disjointedSet::Union(int root1, int root2) {
	if (root1 == root2) return;
	if (parent[root1] > parent[root2]) {
		/*判断树的规模--绝对值*/
		parent[root2] += parent[root1];
		parent[root1] = root2;
	}
	else {
		parent[root1] += parent[root2];
		parent[root2] = root1;
	}
}

/**
*测试函数
*main.cpp
*/

/*
 * @Descripttion:
 * @version:
 * @Author: faker
 * @Date: 2022-10-07 16:39:02
 * @LastEditors: Please set LastEditors
 * @LastEditTime: 2022-10-08 09:51:36
 */
#include "adjListGraph.hpp"
#include <string>
using namespace std;


void test_traverse() {
	const char* s = "abcdefg";
	adjListGraph<char, int> g(7, s);
	g.insert('a', 'b', 10);
	g.insert('a', 'c', 12);
	g.insert('a', 'd', 10);
	g.insert('b', 'd', 12);
	g.insert('b', 'f', 10);
	g.insert('b', 'c', 12);
	g.insert('c', 'd', 10);
	g.insert('d', 'g', 12);
	g.insert('e', 'f', 10);
	g.insert('e', 'g', 12);
	g.insert('g', 'f', 10);

	cout << "深度优先" << endl;
	g.dfs();

	cout << "广度优先" << endl;
	g.bfs();
}

void test_dijstra() {
	const char* s = "abcdefg";
	adjListGraph<char, int> g(7, s);
	g.insert('a', 'b', 10);
	g.insert('a', 'c', 12);
	g.insert('a', 'd', 10);
	g.insert('b', 'd', 12);
	g.insert('b', 'f', 10);
	g.insert('b', 'c', 12);
	g.insert('c', 'd', 10);
	g.insert('d', 'g', 12);
	g.insert('e', 'f', 10);
	g.insert('e', 'g', 12);
	g.insert('g', 'f', 10);

	g.dijstra('a', 0xffff);
}

void test_prim() {
	int array[6] = { 1,2,3,4,5,6 };
	adjListGraph<int, int> g(6, array);
	g.insert(1, 2, 6);
	g.insert(2, 1, 6);
	g.insert(1, 3, 1);
	g.insert(3, 1, 1);
	g.insert(1, 4, 5);
	g.insert(4, 1, 5);
	g.insert(2, 3, 5);
	g.insert(3, 2, 5);
	g.insert(2, 5, 3);
	g.insert(5, 2, 3);
	g.insert(3, 4, 5);
	g.insert(4, 3, 5);
	g.insert(3, 5, 6);
	g.insert(5, 3, 6);
	g.insert(3, 6, 4);
	g.insert(6, 3, 4);
	g.insert(4, 6, 2);
	g.insert(6, 4, 2);
	g.insert(5, 6, 6);
	g.insert(6, 5, 6);
	g.prim(0xff);
}


void test_kruskal() {
	int array[6] = { 1,2,3,4,5,6 };
	adjListGraph<int, int> g(6, array);
	g.insert(1, 2, 6);
	g.insert(2, 1, 6);
	g.insert(1, 3, 1);
	g.insert(3, 1, 1);
	g.insert(1, 4, 5);
	g.insert(4, 1, 5);
	g.insert(2, 3, 5);
	g.insert(3, 2, 5);
	g.insert(2, 5, 3);
	g.insert(5, 2, 3);
	g.insert(3, 4, 5);
	g.insert(4, 3, 5);
	g.insert(3, 5, 6);
	g.insert(5, 3, 6);
	g.insert(3, 6, 4);
	g.insert(6, 3, 4);
	g.insert(4, 6, 2);
	g.insert(6, 4, 2);
	g.insert(5, 6, 6);
	g.insert(6, 5, 6);
	g.kruskal();
}


void test_topSort() {
	string array[7] = { "数学", "程序设计", "离散数学", "软件工程", "数据结构", "数据库", "编译原理" };
	adjListGraph<string, int> g(7, array);
	g.insert(array[0], array[1], 1);
	g.insert(array[0], array[2], 1);
	g.insert(array[2], array[4], 1);
	g.insert(array[2], array[6], 1);
	g.insert(array[1], array[4], 1);
	g.insert(array[1], array[5], 1);
	g.insert(array[1], array[3], 1);
	g.insert(array[4], array[5], 1);
	g.insert(array[4], array[6], 1);
	g.insert(array[5], array[3], 1);
	g.topSort();
}

void test_criticalPath() {
	string array[6] = { "v1", "v2", "v3", "v4", "v5", "v6" };
	adjListGraph<string, int> g(6, array);
	g.insert("v1", "v2", 1);
	g.insert("v1", "v3", 3);
	g.insert("v1", "v4", 5);
	g.insert("v2", "v5", 2);
	g.insert("v2", "v3", 1);
	g.insert("v3", "v4", 1);
	g.insert("v3", "v6", 2);
	g.insert("v4", "v5", 1);
	g.insert("v4", "v6", 2);
	g.insert("v5", "v6", 2);
	g.criticalPath();
}

int main()
{
	//test_traverse();
	//test_dijstra();
	//test_prim();
	//test_kruskal();
	//test_topSort();
	test_criticalPath();
	return 0;
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

橘子味的晚霞和少年

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值