求图的简单路径和回路

下面是用邻接表存储无向图,然后输出图中指定顶点间的指定长度的简单路径,简单路径就是路径中的顶点不重复,还有一个就是求出图中经过某顶点的回路,都是对图的遍历算法的应用,主要是深度优先的遍历,加上简单的回溯。

下面是代码:

//文件"graph.h"

#include <iostream>
#include <string>
#include <queue>
using namespace std;

bool visited[20];
int path[20];

struct ArcNode
{
	int adjvex;
	ArcNode *nextarc;
};

struct VexNode
{
	string data;
	ArcNode *firstarc;
};

class NDGraph
{
private:
	VexNode vertices[20];
	int vexnum;
	int arcnum;
public:
	NDGraph()
	{
		vexnum=0;
		arcnum=0;
	}

	int GetVexNum()
	{
		return vexnum;
	}

	int Locate_Vex(string v)
	{
		for(int i=0;i<vexnum;i++)
			if(vertices[i].data == v)
				return i;
		return -1;
	}

	void Create_NDGraph()
	{
		//构造无向图
		string v1,v2;
		int i,j,k;
		cout<<"输入顶点数和边数:";
		cin>>vexnum>>arcnum;
		while(vexnum>20)
		{
			cout<<"请输入少于20个顶点(重新输入顶点数和边数):";
			cin>>vexnum>>arcnum;
		}

		cout<<"输入顶点名称:";
		for(i=0;i<vexnum;i++)
		{
			cin>>vertices[i].data;
			vertices[i].firstarc=NULL;
		}

		for(k=0;k<arcnum;k++)
		{
			cout<<"输入每条边对应的两个顶点:";
			cin>>v1>>v2;
			
			i=Locate_Vex(v1);
			j=Locate_Vex(v2);

			while(i == -1 || j == -1)
			{
				cout<<"顶点中有不符合要求的,请重新输入:";
				cin>>v1>>v2;
				i=Locate_Vex(v1);
				j=Locate_Vex(v2);
			}

			ArcNode *p=new ArcNode;
			p->adjvex=j;
			p->nextarc=vertices[i].firstarc;
			vertices[i].firstarc=p;

			//置对称边
			ArcNode *q=new ArcNode;
			q->adjvex=i;
			q->nextarc=vertices[j].firstarc;
			vertices[j].firstarc=q;
		}
		cout<<"无向图构造完成"<<endl;
	}

	void DFS_Traverse()
	{
		for(int i=0;i<vexnum;i++)
			visited[i]=false;
		for(i=0;i<vexnum;i++)
			if(!visited[i])
				DFS(i);
	}

	void DFS(int v)
	{
		visited[v]=true;
		cout<<vertices[v].data<<"  ";
		ArcNode *p;
		int w;
		for(p=vertices[v].firstarc;p;p=p->nextarc)
		{
			w=p->adjvex;
			if(!visited[w])
				DFS(w);
		}
	}

	void BFS_Traverse()
	{
		for(int i=0;i<vexnum;i++)
			visited[i]=false;
		for(i=0;i<vexnum;i++)
			if(!visited[i])
				BFS(i);
	}

	void BFS(int v)
	{
		visited[v]=true;
		cout<<vertices[v].data<<"  ";
		queue<int> qu;
		int w,k;
		ArcNode *p=NULL;
		qu.push(v);
		while(!qu.empty())
		{
			w=qu.front();
			qu.pop();
			for(p=vertices[w].firstarc;p;p=p->nextarc)
			{
				k=p->adjvex;
				if(!visited[k])
				{
					visited[k]=true;
					cout<<vertices[k].data<<"  ";
					qu.push(k);
				}
			}
		}
	}

	void Print_X_Y_Path(int u,int v,int l,int d)
	{
		//求出一条长度为l的从u到v的路径,d刚进来的时候是-1
		int m;
		d++;
		visited[u]=true;
		path[d]=u;

		if(u == v && d == l) //找到一条路径
		{
			for(int i=0;i<l;i++)
				cout<<vertices[ path[i] ].data<<"-->";
			cout<<vertices[ path[i] ].data<<endl;
		}
		else if(u == v && d!=l)
		{
			//出现这种情况直接回溯上一顶点,不浪费时间去DFS
			goto loop;
		}
		else
		{
			ArcNode *p=vertices[u].firstarc; //继续DFS
			while(p)
			{
				m=p->adjvex;
				if(!visited[m])
					Print_X_Y_Path(m,v,l,d);
				p=p->nextarc;
			}
		}
		//恢复环境,使顶点可重新使用
		//路径长度减一
loop:	visited[u]=false;
		d--;
	}

	void Print_X_X_Path(int i,int j,int d)
	{
		//找出从i到i的回路,思想和上面的类似
		int v,k;
		ArcNode *p;
		visited[i]=true;
		d++;
		path[d]=i;
		if(i == j && d>2)
		{
			for(k=0;k<d;k++)
				cout<<vertices[ path[k] ].data<<"-->";
			cout<<vertices[ path[k] ].data<<endl;
		}
		else if(i == j && d==2)  //一条边只可以走一次
			goto lop;
		else
		{
			p=vertices[i].firstarc;
			while(p)
			{
				v=p->adjvex;
				if(!visited[v] || v == j)
					Print_X_X_Path(v,j,d);
				p=p->nextarc;
			}
		}
		
lop:	visited[i]=false;
		d--;
	}

};

主函数文件

#include "graph.h"
#include <iostream>
#include <string>
using namespace std;

int main()
{
	NDGraph G;
	string v1,v2;
	int u,v;
	int num;
	G.Create_NDGraph();

	cout<<"图的深度优先遍历为:";
	G.DFS_Traverse();
	cout<<endl;

	cout<<"图的广度优先遍历为:";
	G.BFS_Traverse();
	cout<<endl;

	cout<<"输入两个顶点名称和一个数字,"<<endl;
	cout<<"将输出长度为输入数字大小的两顶点间路径:";
	cin>>v1>>v2>>num;

	u=G.Locate_Vex(v1);
	v=G.Locate_Vex(v2);
	if(u == -1 || v == -1)
	{
		cout<<"顶点中有不符合要求的,操作失败"<<endl;
	}
	else
	{
		for(int i=0;i<G.GetVexNum();i++)
			visited[i]=false;
		cout<<"顶点 "<<v1<<" 到 "<<v2<<" 长度为 "<<num<<" 的简单路径如下:"<<endl;
		G.Print_X_Y_Path(u,v,num,-1);
	}

	cout<<"输入一个顶点名称,将输出所有经过它的回路:";
	cin>>v1;
	u=G.Locate_Vex(v1);
	if(u == -1)
	{
		cout<<"顶点不存在,操作失败"<<endl;
	}
	else
	{
		for(v=0;v<G.GetVexNum();v++)
			visited[v]=false;
		cout<<"经过顶点 "<<v1<<" 的所有回路如下:"<<endl;
		G.Print_X_X_Path(u,u,-1);
	}
	return 0;
}

测试结果:

输入顶点数和边数:5 6
输入顶点名称:v1 v2 v3 v4 v5
输入每条边对应的两个顶点:v1 v2
输入每条边对应的两个顶点:v1 v4
输入每条边对应的两个顶点:v2 v4
输入每条边对应的两个顶点:v2 v3
输入每条边对应的两个顶点:v3 v5
输入每条边对应的两个顶点:v4 v5
无向图构造完成
图的深度优先遍历为:v1  v4  v5  v3  v2
图的广度优先遍历为:v1  v4  v2  v5  v3
输入两个顶点名称和一个数字,
将输出长度为输入数字大小的两顶点间路径:v1 v3 3
顶点 v1 到 v3 长度为 3 的简单路径如下:
v1-->v4-->v5-->v3
v1-->v4-->v2-->v3
输入一个顶点名称,将输出所有经过它的回路:v4
经过顶点 v4 的所有回路如下:
v4-->v5-->v3-->v2-->v4
v4-->v5-->v3-->v2-->v1-->v4
v4-->v2-->v3-->v5-->v4
v4-->v2-->v1-->v4
v4-->v1-->v2-->v3-->v5-->v4
v4-->v1-->v2-->v4
Press any key to continue
下面是生成的无向图示例:

为了更好得理解回溯的过程,可以画画像下面这样的示意图,比如我求 V1 到 V3的长度为3的路径的过程


图可能和你画的不一样,但是主要就是理清一下思路,不会在一重重的递归中乱掉

可以使用深度优先搜索(DFS)算法来判断有向图中是否存在从顶点i到顶点j的路径。具体步骤如下: 1. 构建邻接矩阵表示有向图,其中矩阵元素a[i][j]表示从顶点i到顶点j是否存在一条有向边。 2. 定义一个数组visited,用于记录每个顶点是否被访问过,初始值均为false。 3. 从顶点i开始执行DFS搜索,具体实现方式为:首先将visited[i]设置为true,然后遍历顶点i的所有邻接点j,如果a[i][j]=true且visited[j]=false,就递归地执行DFS搜索j顶点。 4. 如果在DFS搜索的过程中,发现已经访问到了顶点j,说明从顶点i存在一条路径到顶点j,返回true即可。 5. 如果DFS搜索结束后仍然没有访问到顶点j,说明从顶点i不存在一条路径到顶点j,返回false即可。 下面是使用邻接矩阵和DFS算法判断有向图中是否存在从顶点i到顶点j的路径的示例代码(假设有n个顶点,顶点编号从0到n-1): ``` bool hasPath(int i, int j, bool** a, bool* visited, int n) { if (i == j) return true; // i和j是同一个顶点,直接返回true visited[i] = true; // 标记顶点i已经被访问过 for (int k = 0; k < n; k++) { if (a[i][k] && !visited[k]) { // 如果存在从i到k的有向边,并且顶点k没有被访问过 if (hasPath(k, j, a, visited, n)) { // 递归地搜索顶点k是否能到达顶点j return true; } } } return false; // 搜索结束,未找到从i到j的路径 } bool hasPath(int i, int j, bool** a, int n) { bool* visited = new bool[n]; memset(visited, false, n * sizeof(bool)); // 初始化visited数组 bool result = hasPath(i, j, a, visited, n); // 使用DFS搜索从i到j的路径 delete[] visited; return result; } ``` 其中,hasPath函数是判断从顶点i到顶点j是否存在路径的入口函数。首先创建一个visited数组,用于记录每个顶点是否被访问过。然后调用hasPath(int i, int j, bool** a, bool* visited, int n)函数,该函数是实际执行DFS搜索的函数。最后返回DFS搜索的结果。
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值