图论算法<二>:判断有向图中是否有存在循环 ,以及环的个数和各个环中的元素

1、目的

  判断有向图中是否有存在循环 ,以及环的个数和各个环中的元素。
原理:利用DFS求有向图中所有点任意两点之间的所有路径,然后判断环路径并存起来。其中辅助数组visited[]用来记录路径中是否有重复点。

2、示例效果

2.1 原始数据

在这里插入图片描述
路线起终点整理如下:

// 共计12个顶点,19条边。 (起点,终点,1)最后的1代表起点终点是连通的。
起点,终点,12 4 1
起点,终点,19 10 1
起点,终点,18 11 1
起点,终点,14 12 1
起点,终点,111 12 1
起点,终点,11 2 1
起点,终点,13 2 1
起点,终点,11 3 1
起点,终点,13 4 1
起点,终点,13 6 1
起点,终点,11 5 1
起点,终点,16 5 1
起点,终点,16 7 1
起点,终点,16 9 1
起点,终点,17 9 1
起点,终点,19 10 1
起点,终点,15 8 1
起点,终点,18 7 1
起点,终点,110 11 1

为方便测试:将分支的起终点互换改为:e13:7-6; e3:11-8.

2.2 程序计算效果

在这里插入图片描述

3、算法源码

// CheckGraphCircle.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include <vector>
using namespace std;
const int MAX_Vertex_Num = 500;
template<class VexType, class ArcType>
class MGraph
{
public:
	//创建图
	void CreateGraph();//创建图

	//返回顶点v所在顶点向量中的位置(下标)
	int LocateVex(VexType v);

	//检查图中有无环
	bool CheckCircle(int& nCount,std::vector<vector<int>>& vecCircle);

private:
	VexType m_vexs[MAX_Vertex_Num];//顶点向量
	ArcType m_arcs[MAX_Vertex_Num][MAX_Vertex_Num]; //这里把邻接矩阵类型用模板表示,主要是为了处理有权值的情况,比如:权值可以为小数,也可以为整数
	int m_vexNum;//顶点数
	int m_arcNum;//边数

private:
	void DFS(int x, bool visited[MAX_Vertex_Num], int stack[MAX_Vertex_Num], int& top, bool inStack[MAX_Vertex_Num], int& count, std::vector<vector<int>>& vecCircle);
};

template<class VexType, class ArcType>
void MGraph<VexType, ArcType>::CreateGraph()
{
	//VexType first;
	//VexType Secend;
	//cout << "请输入顶点数:";
	//cin >> vexnum;
	//cout << "请输入边数:";
	//cin >> arcnum;

	m_vexNum = 12;
	cout << "顶点数: 12" << endl;
	m_arcNum = 19;
	cout << "边数: 19" << endl;

	//cout << "请输入各个顶点值:";
	for (int i = 1; i <= m_vexNum; i++)
	{
		//cin >> vexs[i];
		m_vexs[i] = i;
	}

	//初始化邻接矩阵
	for (int i = 0; i < m_arcNum; i++)
	{
		for (int j = 0; j < m_arcNum; j++)
		{
			m_arcs[i][j] = 0;
		}
	}

	// 为边赋值
	m_arcs[2][4] = 1;

	m_arcs[9][10] = 1;

	m_arcs[11][8] = 1;

	m_arcs[4][12] = 1;

	m_arcs[11][12] = 1;

	m_arcs[1][2] = 1;

	m_arcs[3][2] = 1;

	m_arcs[1][3] = 1;

	m_arcs[3][4] = 1;

	m_arcs[3][6] = 1;

	m_arcs[1][5] = 1;

	m_arcs[6][5] = 1;

	m_arcs[7][6] = 1;

	m_arcs[6][9] = 1;

	m_arcs[7][9] = 1;

	m_arcs[9][10] = 1;

	m_arcs[5][8] = 1;

	m_arcs[8][7] = 1;

	m_arcs[10][11] = 1;

	//cout << "请输入边的信息:" << endl;
	//for (int i = 0; i < arcnum; i++)
	//{
	//	cin >> first >> Secend;

	//	//如果边有权值的话,则还应该输入权值
	//	int x = LocateVex(first);
	//	int y = LocateVex(Secend);

	//	arcs[x][y] = 1;//如果是有权的话,这里应该是arc[x][y]=权值
	//}
}

/*
参数:v:表示顶点向量中一个值
函数返回值:函数返回v在顶点向量中的下标
*/
template<class VexType, class ArcType>
int MGraph<VexType, ArcType>::LocateVex(VexType v)
{
	for (int i = 0; i < m_vexNum; i++)
	{
		if (m_vexs[i] == v)
		{
			return i;
		}
	}
	return -1;
}

/*
检查图中是不是有回向边
思想:
如果有回向边,则无环,反之有环
*/
template<class VexType, class ArcType>
bool MGraph<VexType, ArcType>::CheckCircle(int& nCount, std::vector<vector<int>>& vecCircle)
{
	nCount = 0;//环的个数
	int top = -1;
	int stack[MAX_Vertex_Num];
	bool inStack[MAX_Vertex_Num] = { false };
	bool visited[MAX_Vertex_Num] = { false };
	for (int i = 0; i < m_vexNum; i++)
	{
		if (!visited[i])
		{
			DFS(i, visited, stack, top, inStack, nCount, vecCircle);
		}
	}

	return (nCount > 0) ? true : false;
}

bool CompareVector(vector<int> vec1, vector<int> vec2)
{
	if (vec1.size() != vec2.size())
	{
		return false;
	}

	if(vec1.size() == 0)
	{
		return true;
	}

	int nFirstValue = vec1[0];
	int nIndex = -1;
	for (int i = 0; i < vec2.size(); ++i)
	{
		if (vec2[i] == nFirstValue)
		{
			nIndex = i;
			break;
		}
	}

	int nCounter = 0;
	vector<int> vecMid;
	for (int i = 0; i < vec2.size(); ++i)
	{
		if (nIndex > vec2.size() - 1)
		{
			nIndex = 0;
		}
		vecMid.push_back(vec2[nIndex]);
		nIndex++;
	}

	for (int i = 0; i < vec1.size(); ++i)
	{
		if (vec1[i] != vecMid[i])
		{
			return false;
		}
	}

	return true;
}

template<class VexType, class ArcType>
void MGraph<VexType, ArcType>::DFS(int x, bool visited[MAX_Vertex_Num], int stack[MAX_Vertex_Num], int& top, bool inStack[MAX_Vertex_Num], int& nCount, std::vector<vector<int>>& vecCircle)
{
	visited[x] = true;
	stack[++top] = x;
	inStack[x] = true;
	for (int i = 0; i < m_vexNum; i++)
	{
		if (m_arcs[x][i] != 0)//有边
		{
			if (!inStack[i])
			{
				DFS(i, visited, stack, top, inStack, nCount, vecCircle);
			}
			else //条件成立,表示下标为x的顶点到 下标为i的顶点有环
			{
				nCount++;
				//cout << "第"<< nCount << "环为:";
				//从i到x是一个环,top的位置是x,下标为i的顶点在栈中的位置要寻找一下
				//寻找起始顶点下标在栈中的位置
				int t = 0;
				for (t = top; stack[t] != i; t--);
				//输出环中顶点
				vector<int> vecNode;
				for (int j = t; j <= top; j++)
				{
					//cout << (int)m_vexs[stack[j]];
					vecNode.push_back((int)m_vexs[stack[j]]);
				}
				//cout << endl;

				bool bHas = false;
				for (int i = 0; i < vecCircle.size(); i++)
				{
					vector<int> vecTemp = vecCircle[i];
					if (CompareVector(vecTemp, vecNode))
					{
						bHas = true;
						break;
					}
				}

				if (!bHas)
				{
					vecCircle.push_back(vecNode);
				}
			}
		}
	}
	//处理完结点后,退栈
	top--;
	inStack[x] = false;
}

int main()
{
	MGraph<char, int> myGraph;
	myGraph.CreateGraph();

	int nCount = 0;
	std::vector<vector<int>> vecCircle;
	if (myGraph.CheckCircle(nCount, vecCircle))
	{
		cout << "当前图中存在局部循环个数:" << vecCircle.size() << endl;
	}
	else
	{
		cout << "当前图中不存在局部循环!" << endl;
	}

	for (int i = 0; i < vecCircle.size(); i++)
	{
		cout << "第" << i << "个环为:";

		vector<int> vecTemp = vecCircle[i];
		for (int j = 0; j < vecTemp.size(); j++)
		{
			cout << vecTemp[j] << " ";
		}

		cout << endl;
	}
	
	return 1;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
4.1 SNGraph 一 点、向量 基本运算 直线(线段、射线) 直线(线段、射线)用起点、方向(单位向量)、线段长度表示。 包括如下功能:  点是否在直线上。  假定点在直线上,点到直线起点的有向距离。如果点在直线上,点到直线距离为n。如果n>=0,则点在射线上;如果(n>=0)&&(n <= 线段长度) ,则点在线段上。  两直线是否平行或重合。  两直线是否重合。  两直线是否垂直。  两直线交点。  两非平行直线距离。  求垂足。 三 平面 通过过平面一点和方向(单位矢量)表示平面。包括如下功能:  点到平面的有向距离。通过平面标准法向量和距离,可以求垂足;通过点到平面的距离的正负,可以看出多个点是否在同侧;如果点到平面的距离为0,则点在平面上,否则不在平面上。  直线是否在平面上  平面和直线的交点 通过调用其他功能可以实现的功能:  平面的法向量平行于直线,则平面和直线垂直  平面的法向量垂直于直线,则平面和直线平行  平面的法向量平行(垂直)则平面平行(垂直)  平行平面的距离等于平面任意一点到另一平面的距离 四 矩阵 包括以下功能:  初始化为单位矩阵。  为向x,y,z方向缩放建立矩阵。  为任意方向缩放建立矩阵。投影平面,可以通过向平面法线方向缩放0实现。平面镜像,可以通过向平面法线方向缩放-1实现。  为对一个点镜像建立矩阵。  为对一条直线镜像建立矩阵。  为对一条对称轴旋转建立矩阵。  求对应行列式的值。  求逆矩阵。  求转置矩阵。  左乘。  求对应行列式的代数余子式。  常见运算符。 4.2 SN 封装了许多基础的功能。 一 接口  读写锁。 避免依赖其它类库 有些类经常用于库间接口,所以需要避免依赖其它类库。  字符串类、函数,比如:宽字符、多字符间的转换。  时间类。  数组的封装。 三 其它  将错误信息记录到全局变量,应用场景:构造函数和析构函数throw会引起不可预料的问题。  安全缓存,额外开辟若干个字节的空间,并初始化为一个特定值,如果不越界,这些值不会改变。  智能指针,为了将关联降为依赖。CAutoPtr<C> m_pC代替C m_c,头文件不需要引用C类的头文件。只需要声明C类,在源文件引用C类的头文件。  MD5。  RSA。  SHA。  考虑溢出的加减法。比如:int型的10亿加20亿,-10亿减20亿。  通过表名、列名、某些列的值生成sql语句。  安全指针和防野指针类。防野指针类:在构造函数将状态初始为已初始化,在析构函数将状态设置为已释放。安全指针在使用时之前判断 防野指针类释放是“已初始化”,否则抛出异常。  将有参数的函数统一成没参数返回值类型void的仿函数。  遍历文件夹的文件和子文件夹。  随机数和排列组合。  系列化和反系列化。将对象和变量转化成进制,并将进制转回变量和对象。  拆分,如字符串。 4.3 SNMFC 一 网络功能  网络基本功能:如获取本机IP,通过域名获取IP,IE版本。  HTML对话框的封装类。  用于服务端的,带“回调类”的绑定监听类,利用IO完成端口。  用于客户端的,带“回调类”连接类,利用select模式完成,可以指定是否开启新线程。连接时,可以指定超时时间,默认5秒。如果直接调用系统的connect,超时时间是75秒。  能够自动处理“粘包”、“拆包”的进制解析类。  安全套接字的辅助类,如:设置发送、连接超时。  比较服务端的某个文件夹和客户端的某个文件夹,并更新那些md5不同的文件。 多线程  用临界区实现的线程锁,和线程读写锁。  窗口辅助类。  开启一个线程并调用一个函数。  开启一个线程并循环调用一个函数。  支持多线程的日志。  启动一个线程,等待若干秒后,Post或Send一个消息后,结束线程。 三 界面  三态树控件。  列表框扩展类和函数。  树控件的扩展。  组合框的扩展。  关于窗口功能的封装。比如:从左到右依次排列子窗口,排不下则下一行。可以指定行间距。页眉和页脚是行间距的一半。  位的加载和显示。 四 其它  Ini文件。  数组封装类。  获取硬件信息,如网卡。  文件或文件夹的常用功能。  注册表的扩展。 4.4 SNSTL  数组(向量)扩展。  用于多线程的向量。  JSON解析。  集合的扩展。  映射的扩展。  指针向量,可以存派生类。  指针映射,可以存派生类。 4.5 其它库  UnitTest,本机单元测试项目,对整个库的重要功能进行单元测试。  SNBCG,著名界面库的扩展,几乎没使用。  SNPicture,像的处理(如转换bmp格式),几乎没使用。  SNMath,数学及数据结构库,几乎没使用。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

欧特克_Glodon

打赏我五毛,我吃饱接着搞!

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

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

打赏作者

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

抵扣说明:

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

余额充值