【数据结构】图的表示以及遍历的C++实现

注意stringstream 类的使用 完成string到int的类型转化
先clear再str("")实现重复使用
typename Graph

稀疏图的邻接表实现

#pragma once
#include <iostream>
#include<cassert>
#include<vector>
using namespace std;
class SparseGraph
{
private:
	int n, m;//点的个数和边的个数
	bool isdirected;//是否是有向图
	vector<vector<int>> g;//存储INT类型变量 点
public:
	SparseGraph(int n, bool isdirected)
	{
		this->isdirected = isdirected;
		this->n = n;
		g.resize(n);//和下一句作用一样
		
		/*for (int i=0;i<n;i++)
		{
			g.push_back(vector<int>());//vector的构造函数 尺寸为n 值为false
		}*/
	}
	~SparseGraph()
	{}
	int V() { return n; }//返回顶点数
	int E() { return m; }//返回边的数
	void addEdge(int p, int q)
	{
		assert(p >= 0 && p < n);
		assert(q >= 0 && q < n);
		//if (hasEdge(p, q))return;//不考虑边重复的问题
		g[p].push_back(q);
		if (!isdirected&&p!=q)//无向图 p q不能相等 否则就增加了两次同一条边
			g[q].push_back(p);
		m++;//边数目增加
	}
	bool hasEdge(int p, int q)//p->q的类型
	{
		assert(p >= 0 && p < n);
		assert(q >= 0 && q < n);
		for (int i = 0;i < g[p].size();i++)
		{
			if (g[p][i]==q)return true;
		}
		return false;
	}
	class adjIterator
	{
	private:
		SparseGraph& graph;
		int v;
		int index;
	public:
		adjIterator(SparseGraph& gra, int v):graph(gra)//初始化列表
		{
			this->v = v;
			index =-1;
		}
		~adjIterator(){}
		int begin()
		{
			assert(v >= 0 && v < graph.g.size());
			index = 0;
			if (graph.g[v].size())
			{
				return graph.g[v][0];
			}
			return -1;
		}
		int next()
		{
			assert(v >= 0 && v < graph.g.size());
			index++;
			if (index < graph.g[v].size())
			{
				return graph.g[v][index];
			}
			return -1;
		}
		bool end()
		{
			
			return (index >= graph.g[v].size());
		}
	};
	void show()
	{
		for (int i = 0;i < n;i++)
		{
			cout << i << ":";
			for (int j = 0;j < g[i].size();j++)
			{
				cout << g[i][j] << " ";
			}
			cout << endl;
		}
		cout << endl;
	}

};

稠密图的邻接矩阵实现

#pragma once
#include <iostream>
#include<cassert>
#include<vector>
using namespace std;
class DenseGraph
{
private:
	int n, m;//点的个数和边的个数
	bool isdirected;
	vector<vector<bool>> g;
public:
	DenseGraph(int n,bool isdirected)
	{
		this->isdirected = isdirected;
		this->n = n;
		//g.resize(n);
		for (int i=0;i<n;i++)
		{
			g.push_back(vector<bool>(n, false));//vector的构造函数 尺寸为n 值为false
		}
	}
	~DenseGraph()
	{}
	int V() { return n; }//返回顶点数
	int E() { return m; }//返回边的数
	void addEdge(int p, int q)
	{
		if (hasEdge(p, q))return;//如果有就直接返回 不用进行之下的步骤了 避免重复添加count
		g[p][q] = true;
		if (!isdirected)//无向图
			g[q][p] = true;
		m++;//边数目增加
	}
	bool hasEdge(int p, int q)
	{
		assert(p >= 0 && p < n);
		assert(q >= 0 && q < n);
		return g[p][q];
	}
	class adjIterator
	{
	private:
		DenseGraph& graph;
		int v;
		int index;
	public:
		adjIterator(DenseGraph& gra, int v) :graph(gra)
		{
			assert(v >= 0 && v < graph.g[v].size());
			this->v = v;
			this->index = 0;
		}
		~adjIterator(){}
		int begin()
		{
			int i;
			for ( i = 0;i < graph.g[v].size();i++)
			{
				if (graph.g[v][i] == true)break;
			}
			index = i;
			return index;
		}
		int begin2()
		{
			index = -1;
			return next2();//非常机智啊 可以直接调用next
		}
		int next()
		{
			int i;
			for (i = index + 1;i < graph.g[v].size();i++)
			{
				if (graph.g[v][i] == true)break;
			}
			index = i;
			if (i == graph.g[v].size())return -1;
			return index;
		}
		int next2()
		{
			for (index++;index < graph.V();index++)//不用其他变量 直接用index
			{
				if (graph.g[v][index] == true)return index;//直接返回 找到了就提前返回 不用判断是否没找到了
			}
			//此时index=graph.V();
			return -1;
		}
		bool end()
		{
			return (index >= graph.g[v].size());
		}

	};
	void show()
	{
		for (int i = 0;i < n;i++)
		{
			cout << i << ":";
			for (int j = 0;j < n;j++)
			{
				cout << g[i][j] << " ";
			}
			cout << endl;
		}
		cout << endl;
	}
};

模板类实现各种类型图的测试

从文件中读取图

#pragma once
#include <iostream>
#include<cassert>
#include<fstream>
#include<string>
#include<vector>
#include<sstream>
using namespace std;

template <typename Graph>
class ReadGraph
{
public:
	ReadGraph(const string &filename, Graph &graph)//构造函数
	{
		ifstream infile(filename);
		string line;
		int V, E;
		
		assert(infile.is_open());//确认打开
		assert(getline(infile, line));
		stringstream outstring;//构造函数 stringstream的用法
		outstring << line;
		outstring >> V >> E;
		outstring.clear();
		outstring.str("");
		
		int v, w;
		assert(graph.V() == V);
		for (int i = 0;i < E;i++)
		{
			assert(getline(infile, line));
			//outstring.str(line);
			//stringstream outstring(line);
			outstring << line;
			outstring >> v >> w;
			outstring.clear();
			outstring.str("");
			graph.addEdge(v, w);
		}
	}
};

深度优先搜索遍历

#pragma once
#include <iostream>
#include<cassert>
#include<fstream>
#include<string>
#include<vector>
#include<sstream>
using namespace std;

template <typename Graph>
class Component
{
private:
	Graph& graph;
	bool* visited;//是否访问
	int ccount;//一共多少联通分量 组
	int* id;//属于第几个联通分量 用于计算
	void dfs(int p)
	{
		visited[p] = true;
		id[p] = ccount;
		typename Graph::adjIterator iter(graph,p);//修改 加template
		for (int w = iter.begin();!iter.end();w = iter.next())
		{
			if (!visited[w])
			{
				dfs(w);
			}
		}
		
	}
public:
	Component(Graph& graph) :graph(graph)
	{
		ccount = 0;
		int n = graph.V();
		visited = new bool[n];
		id = new int[n];
		for (int i = 0;i < n;i++)
		{
			visited[i] = false;//初始化
			id[i] = -1;
		}
		for (int i = 0;i < n;i++)
		{
			if (!visited[i])//没有被访问过
			{
				dfs(i);
				ccount++;//更新一个新的组
			}
				
		}
	}
	~Component()
	{
		delete[] visited;
		delete[] id;
		ccount = 0;
	}
	int count()
	{
		return ccount;
	}
	bool isConnected(int p,int q)
	{
		return id[p] == id[q];
	}
};

深度优先搜索遍历并输出查询路径

count遍历是自己加的用来计算是否有环

#pragma once
#include <iostream>
#include<cassert>
#include<fstream>
#include<string>
#include<vector>
#include<sstream>
#include<stack>
using namespace std;
//有向图依然有效
template <typename Graph>
class Path
{
private:
	Graph& graph;
	bool* visited;
	int* from;
	int  p;
	int  ccount;
	void dfs(int num)
	{
		visited[num] = true;
		typename Graph::adjIterator iter(graph, num);//注意语法 
		for (int w = iter.begin();!iter.end();w = iter.next())
		{
			if (visited[w] && from[num] != w) 
			{ ccount++; }
			if (!visited[w])//如果没有访问过
			{
				from[w] = num;
				dfs(w);
			}
		}
	}
public:
	Path(Graph& g, int p) :graph(g)
	{
		ccount == 0;
		this->p = p;
		int n = graph.V();
		assert(p >= 0 && p < n);
		visited = new bool[n];
		from = new int[n];
		for (int i = 0;i < graph.V();i++)
		{
			visited[i] = false;
			from[i] = -1;
		}
		dfs(p);
	}
	~Path()
	{
		delete[] from;
		delete[] visited;
	}
	bool hasPath(int w)
	{
		assert(w >= 0 && w < graph.V());
		return visited[w];//被访问过说明有路径
	}
	void path(int w, vector<int>& vec)
	{
		assert(w >= 0 && w < graph.V());
		stack<int> sta;
		sta.push(w);
		while (from[w] >= 0)
		{
			sta.push(from[w]);
			w = from[w];
		}
		/*while (w != -1)
		{
			sta.push(w);
			w = from[w];
		}*/
		vec.clear();
		while (!sta.empty())
		{
			vec.push_back(sta.top());
			sta.pop();
		}
	}
	void showPath(int p)
	{
		vector<int> vec;
		path(p, vec);
		cout << "path:";
		for (int i = 0;i < vec.size();i++)
		{

			cout << vec[i];
			if (i == vec.size() - 1)
			{
				cout << endl;
				break;
			}
			cout << "->";
		}
	}
	//int circount() { return ccount/2; }
};

广度优先搜索实现图的遍历和路径表示

#pragma once
#include <iostream>
#include<cassert>
#include<fstream>
#include<string>
#include<vector>
#include<sstream>
#include<stack>
#include<queue>
using namespace std;
//有向图依然有效
template <typename Graph>
class ShortPath
{
private:
	Graph& graph;
	bool* visited;
	int* from;
	int  p;
	int* length;//距离根节点的距离

public:
	ShortPath(Graph& g, int p) :graph(g)
	{
		//ccount == 0;
		this->p = p;
		int n = graph.V();
		assert(p >= 0 && p < n);
		visited = new bool[n];
		from = new int[n];
		length = new int[n];
		for (int i = 0;i < graph.V();i++)
		{
			visited[i] = false;
			from[i] = -1;
			length[i] = -1;
		}
		queue<int> q;
		q.push(p);
		visited[p] = true;
		length[p] = 0;
		while (!q.empty())
		{
			int tmp = q.front();
			q.pop();
			typename Graph::adjIterator iter(graph, tmp);
			for (int w = iter.begin();!iter.end();w = iter.next())
			{
				if (!visited[w])//记得 没有visited的才遍历
				{
					q.push(w);
					visited[w] = true;
					from[w] = tmp;
					length[w] = length[tmp] + 1;
				}
			}
		}
	}
	~ShortPath()
	{
		delete[] length;
		delete[] from;
		delete[] visited;
	}
	bool hasPath(int w)
	{
		assert(w >= 0 && w < graph.V());
		return visited[w];//被访问过说明有路径
	}
	void path(int w, vector<int>& vec)
	{
		assert(w >= 0 && w < graph.V());
		stack<int> sta;
		sta.push(w);
		while (from[w] >= 0)
		{
			sta.push(from[w]);
			w = from[w];
		}
		/*while (w != -1)
		{
			sta.push(w);
			w = from[w];
		}*/
		vec.clear();
		while (!sta.empty())
		{
			vec.push_back(sta.top());
			sta.pop();
		}
	}
	void showPath(int p)
	{
		vector<int> vec;
		path(p, vec);
		cout << "path:";
		for (int i = 0;i < vec.size();i++)
		{

			cout << vec[i];
			if (i == vec.size() - 1)
			{
				cout << endl;
				break;
			}
			cout << "->";
		}
	}
	int rlength(int w) {
		assert(w >= 0 && w < graph.V());
		return length[w];
	}

};

测试函数

#include <iostream>
#include<cassert>
#include<vector>
#include<ctime>
#include"ReadGraph.h"
#include"DenseGraph.h"
#include"SparseGraph.h"
#include"DFS.h"
#include"path.h"
#include"ShortPath.h"
using namespace std;
int main()
{
	SparseGraph sGraph(7, false);
	DenseGraph  dGraph(7, false);
	string filename1 = "testG2.txt";
	ReadGraph<SparseGraph> read(filename1, sGraph);
	sGraph.show();
	ShortPath<SparseGraph> path1(sGraph, 0);
	path1.showPath(3);
	//cout << path1.circount() << endl;
	ReadGraph<DenseGraph> read2(filename1, dGraph);
	dGraph.show();
	//生成随机的图/
	int N, M;
	N = 20;
	M = 100;
	srand(time(NULL));

	SparseGraph sgraph(N, false);
	DenseGraph  dgraph(N, false);
	for (int i = 0;i < M;i++)
	{
		int p = rand() % N;
		int q = rand() % N;
		sgraph.addEdge(p, q);
		dgraph.addEdge(p, q);
	}
	for (int v = 0;v < N;v++)
	{
		cout << v << ":";
		SparseGraph::adjIterator iter(sgraph,v);
		int w=iter.begin();
		bool t = iter.end();
		for ( w = iter.begin();!iter.end();w = iter.next())
		{
			cout << w << " ";
		}
		cout << endl;
	}
	for (int v = 0;v < N;v++)
	{
		cout << v << ":";
		DenseGraph::adjIterator iter(dgraph, v);
		for (int w = iter.begin();!iter.end();w = iter.next())
		{
			cout << w << " ";
		}
		cout << endl;
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值