数据结构——图的基本操作(采用邻接表来储存)

图,是数据结构中的重要知识点 ,本文用c++代码详细展示了图的一些基本操作,包括图的创建,深度优先遍历,广度优先遍历,以及基于DFS和BFS来解决的一些拓展问题(代码中有注释),详见代码👇

#include<iostream>
#include<string>
#include<vector>
#include<queue>
#include<stack>
#define MAXV 20		//最大结点数量
const int INF = 0x3f3f3f3f;
using namespace std;

struct ArcNode		//边结点类型
{
	int adjvex;		//邻接点的数值(索引)
	int weight;
	ArcNode* nextarc;
};

struct HNode		//头结点类型
{
	string info;		//包含的信息
	ArcNode* firstarc;		
};

class AdjGraph		//用邻接表储存图
{
public:
	HNode adjlist[MAXV];
	int n, e;
	int visited[MAXV];		//用于标识对应索引的结点是否访问过,没有访问为0,访问过为1

	AdjGraph()		//构造函数
	{
		for (int i = 0; i < MAXV; i++)
		{
			adjlist[i].firstarc = NULL;
			visited[i] = 0;
		}
	}

	~AdjGraph()		//析构函数
	{
		ArcNode* p, * pre;
		for (int i = 0; i < n; i++)
		{
			pre = adjlist[i].firstarc;
			if (pre != NULL)	
			{
				p = pre->nextarc;
				while (p != NULL)
				{
					delete pre;
					pre = p;
					p = p->nextarc;
				}
				delete pre;
			}
		}
	}

	void CreateAdjGraph(int a[][MAXV], int n, int e)		//创建图
	{
		ArcNode* p;
		this->n = n;
		this->e = e;
		for (int i = 0; i < n; i++)
		{
			for (int j = n - 1; j >= 0; j--)
			{
				if (a[i][j] != 0 && a[i][j] != INF)
				{
					p = new ArcNode();
					p->adjvex = j;
					p->weight = a[i][j];
					p->nextarc = adjlist[i].firstarc;
					adjlist[i].firstarc = p;
				}
			}
		}
	}

	void DispAdjGraph()		//打印图
	{
		cout << "(邻接点索引,该边权值)" << endl;
		ArcNode* p;
		for (int i = 0; i < n; i++)
		{
			printf(" [%d]", i);
			p = adjlist[i].firstarc;
			if (p != NULL)
				cout << " → ";
			while (p != NULL)
			{
				printf("(%d,%d) ", p->adjvex, p->weight);
				p = p->nextarc;
			}
			printf("\n");
		}
	}

	void ClearTag()
	{
		for (int i = 0; i < MAXV; i++)
		{
			visited[i] = 0;
		}
	}

	void DFS(AdjGraph& G, int v)
	{
		ClearTag();
		DFS1(G, v);
	}
	void DFS0(AdjGraph& G, int v)	//区别于DFS1,DFS0只遍历,不打印
	{
		visited[v] = 1;
		ArcNode* p = G.adjlist[v].firstarc;

		while (p != NULL)
		{
			int w = p->adjvex;		
			if (visited[w] == 0)		
				DFS0(G, w);
			p = p->nextarc;
		}
	}
	void DFS1(AdjGraph& G, int v)	//**连通图**的深度优先遍历,起始点为v,深度优先遍历符合栈的特征,可使用栈或递归来实现
	{
		cout << v << " ";
		visited[v] = 1;
		ArcNode* p = G.adjlist[v].firstarc;

		while (p != NULL)
		{
			int w = p->adjvex;		//当前结点的邻接点索引
			if (visited[w] == 0)		//若该邻接点未访问,则访问它
				DFS1(G, w);
			p = p->nextarc;
		}
	}
	void DFSA(AdjGraph& G)	//**非连通图**的深度优先遍历
	{
		ClearTag();
		for (int i = 0; i < G.n; i++)	//按顺序以每一个结点为起始点,若没有访问过,则进行DFS
		{
			if (visited[i] == 0)
				DFS1(G, i);
		}
	}

	void BFS(AdjGraph& G, int v)
	{
		ClearTag();
		BFS1(G, v);
	}
	void BFS1(AdjGraph& G, int v)	//**连通图**的广度优先遍历,起始点为v,广度优先遍历符合队列的特征,使用队列来实现
	{
		queue<int> qu;
		cout << v << " ";
		visited[v] = 1;
		qu.push(v);
		while (!qu.empty())
		{
			int u = qu.front();
			qu.pop();
			ArcNode* p = G.adjlist[u].firstarc;
			while (p != NULL)
			{
				if(visited[p->adjvex] == 0)
				{
					cout << p->adjvex << " ";
					qu.push(p->adjvex);
					visited[p->adjvex] = 1;
				}
				p = p->nextarc;
			}
		}
	}
	void BFSA(AdjGraph& G)	//**非连通图**的广度优先遍历
	{
		ClearTag();
		for (int i = 0; i < G.n; i++)	//按顺序以每一个结点为起始点,若没有访问过,则进行BFS
		{
			if (visited[i] == 0)
				BFS1(G, i);
		}
	}

	bool IsConnected(AdjGraph& G)	//判断无向图是否连通
	{
		ClearTag();
		DFS0(G, 0);		//先从结点0开始进行一次DFS
		for (int i = 0; i < G.n; i++)
		{
			if (visited[i] == 0)	//如果发现有没访问过的结点,说明该无向图不连通
				return false;
		}
		return true;
	}
	
	bool HasPath(AdjGraph& G, int u, int v)		//基于深度优先遍历,查找是否存在从u到v的简单路径
	{
		ClearTag();
		return HasPath1(G, u, v);
	}
	bool HasPath1(AdjGraph& G, int u, int v)
	{
		visited[u] = 1;
		ArcNode* p = G.adjlist[u].firstarc;

		while (p != NULL)
		{
			int w = p->adjvex;
			if (w == v)
				return true;
			else if (visited[w] == 0)
			{
				if(HasPath1(G, w, v))
					return true;
			}
			p = p->nextarc;
		}
		return false;
	}
	void FindaPath(AdjGraph& G, int u, int v)
	{
		ClearTag();
		vector<int> path;
		FindaPath1(G, u, v, path);
	}
	void FindaPath1(AdjGraph& G, int u, int v, vector<int> path)	//找出一个从u到v的简单路径,注意path不能是引用参数
	{
		visited[u] = 1;
		path.push_back(u);
		if (u == v)
		{
			for (int i = 0; i < path.size(); i++)
				cout << path[i] << " ";
			cout << endl;
			return;
		}
		ArcNode* p = adjlist[u].firstarc;
		while (p != NULL)
		{
			int w = p->adjvex;
			if (visited[w] == 0)
				FindaPath1(G, w, v, path);
			p = p->nextarc;
		}
	}

	bool HasCycle(AdjGraph& G)
	{
		for(int i = 0;i < G.n;i++)		//轮流以每一个结点为起始点找路径,看是否有回路
		{
			vector<int> path;
			vector<int> inpath(G.n, 0);
			if (HasCycle1(G, i, path, inpath))
				return true;
		}
		return false;
	}
	bool HasCycle1(AdjGraph& G, int u, vector<int> path, vector<int> inpath)	 //判断有向图中是否存在任何简单回路
	{
		inpath[u] = 1;
		ArcNode* p = adjlist[u].firstarc;
		while (p != NULL)
		{
			int w = p->adjvex;
			if (inpath[w] == 0)
			{
				if (HasCycle1(G, w, path, inpath))
					return true;
			}
			else
				return true;
			p = p->nextarc;
		}
		return false;
	}

};

int main()
{
	AdjGraph G;
	int n = 5, e = 5;
	int A[MAXV][MAXV] = { {0,8,INF,5,INF},{INF,0,3,INF,INF},{INF,INF,0,INF,6},{INF,INF,9,0,INF},{INF,INF,INF,INF,0} };
	G.CreateAdjGraph(A, n, e);
	cout << "图的邻接表:\n"; G.DispAdjGraph();
	cout << "\n";

	cout << "一个深度优先遍历(连通图):" << endl;
	G.DFS(G, 0);
	cout << endl;
	cout << "一个深度优先遍历(非连通图):" << endl;
	G.DFSA(G);
	cout << endl;
	if (G.IsConnected(G))
		cout << "该无向图是连通的" << endl;
	else
		cout << "该无向图不连通" << endl;
	cout << endl;

	cout << "一个广度优先遍历(连通图):" << endl;
	G.BFS(G, 0);
	cout << endl;
	cout << "一个广度优先遍历(非连通图):" << endl;
	G.BFSA(G);
	cout << endl;
	
	cout << "输入你要查找的路径(起始点和终点):";
	int u, v;
	cin >> u;
	cin >> v;
	if (G.HasPath(G, u, v))
	{
		cout << "存在从" << u << "到" << v << "的简单路径,其中一条简单路径为:";
		G.FindaPath(G, u, v);
		cout << endl;
	}
	else
		cout << "不存在从" << u << "到" << v << "的简单路径" << endl;
	if (G.HasCycle(G))
		cout << "该有向图存在回路" << endl;
	else
		cout << "该有向图不存在回路" << endl;



	return 0;
}

欢迎各位读者留下宝贵的意见! 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值