图论基础及C++实现

一、图的定义和基本术语

大家可以参考https://oi-wiki.org/
我就不多赘述了,定义术语之类的到处都是,这里我主要给出代码吧,图的基本功能的实现。

首先,我们来看看基本功能有哪些
在这里插入图片描述
接下来我们来看看实现的代码。
我采用的是结构体,然后构建的是无向图用邻接矩阵实现。

#include<iostream>
#include<string>
using namespace std;
#define max_  10
//最大顶点数
int visit[max_];
 /*=========================结构定义*/
struct Graph
{
	string vex;
	int arc[max_][max_];
	int vexnum, edgenum;
};
Graph G;
void CreateG(void)	//================建立邻接矩阵
{
	int i, j, a;
	cout << "输入顶点数和边数:" << endl;
	cin >> G.vexnum >> G.edgenum;
	cout << "输入顶点信息" << endl;
	//for (i = 0; i < G.vexnum; i++)  //顶点信息
		cin >> G.vex;
	for (i = 0; i < G.vexnum; i++)
		for (j = 0; j < G.edgenum; j++)
			G.arc[i][j] = 0;        //初始化邻接表
	for (a = 0; a < G.edgenum; a++)
	{
		cout << "输入边的下标i,下标j" << endl;
		cin >> i >> j;
		G.arc[i][j] = 1; G.arc[j][i] = 1;
	}
	for (i = 0; i < G.vexnum; i++)
	{
		for (j = 0; j < G.vexnum; j++)
			cout << G.arc[i][j]<<"  ";
		cout << endl;
	}
}

然后是一些基本的操作功能,可以对照我们上面给出的图。

void Locatevex(char a)//=================查找
{
	int  m = 0;
	while (m<G.vexnum)
	{
		if (a == G.vex[m])
		{
			cout << "该点在第" << m + 1 << "位" << endl; break;
		}
		m++;
	}
	if (m == G.vexnum)
		cout << "该点不存在" << endl;
}
char Getvex(int m)
{
	return G.vex[m];
}
string Putvxe(int a)
{
	string m="value";
	string::iterator it = G.vex.begin();
	for (; *it != ' '; it++)
	{
		if (*it == G.vex[a])
		{
			G.vex.erase(it);
			break;
		}
	}
	G.vex.insert(a, m);
	return G.vex;
}
int Firstadj(int a)
{
	int j = 0;
	for (; j < G.vexnum; j++)
	{
		if (G.arc[a][j] == 1)
			 break;
	}
	if (j == G.vexnum)
		return  NULL;
	else
		return j;
}
int Nextadj(int a)
{
	int i = 0, j = 0;
	for (; i < G.vexnum; i++)//找v的邻接点w
	{
		if (G.arc[a][i] == 1)
		{
			for (; j < G.vexnum; j++)//找w的邻接点
			{
				if (G.arc[j][i] == 1)
					break;
			}
			break;
		}
	}
	if (i != G.vexnum && j != G.vexnum && a != j)
		return j;
	else
		return NULL;
}
void Insertvex()
{
	char a;
	cout << "请输入增加的新顶点" << endl;
	cin >> a;
	G.vex =G.vex + a;
}
void Deletevex()
{
	int v;
	cout << "请输入需要删除的顶点" << endl;
	cin >> v;
	string::iterator it = G.vex.begin();
	for (; *it != '\0'; it++)//删除顶点
	{
		if (*it == G.vex[v])
		{
			G.vex.erase(it);
			break;
		}
	}
	for (int i = 0; i < G.vexnum; i++)//删除相关的弧
	{
		if (G.arc[v][i] == 1)
		{
			G.arc[v][i] = 0; G.arc[i][v] = 0;
		}
	}
}
void Insertarc()
{
	int i, j;
	cout << "请输入需要增添的弧的两个下标" << endl;
	cin >> i >> j;
	G.arc[i][j] = 1; G.arc[j][i] = 1;
}
void Deletearc()
{
	int i, j;
	cout << "请输入需要删除的弧的两个下标" << endl;
	cin >> i >> j;
	G.arc[i][j] = 0; G.arc[j][i] = 0;
}

然后就是我们的深搜广搜

void DFS(int v)
{
	int j;
	visit[v] = 1;
	cout << G.vex[v];
	for (j = 0; j < G.vexnum; j++)
		if (G.arc[v][j] == 1 && !visit[j])
			DFS(j);
}
void DFSTraverse()
{
	int i;
	for (i = 0; i < G.vexnum; i++)//对所有点进行初始化
		visit[i] = 0;
	for (i = 0; i < G.vexnum; i++)
		if (!visit[i])
			DFS(i);
}
void BFS(int v)
{
	int i, j, n = 0,*p;
	int a[max_];
	p = a;
	for (i = 0; i < G.vexnum; i++)
		visit[i] = 0;
	visit[v] = 1;
	a[n++] = v;
	for (i = 0; i < G.vexnum; i++)
	{
		if (*p != '/0')
		{
		    for (j = 0; j < G.vexnum; j++)
			{
				if (G.arc[*p][j] == 1 && !visit[j])
				{
					visit[j] = 1;
					a[n++] = j;
				}
			}
			p++;
		}
	}
	for (n = 0; n < G.vexnum; n++)
		cout << G.vex[a[n]];
}

作为一个菜的不能再菜的菜鸟,希望我能慢慢进步吧。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
<项目介绍> 基于C++实现的改进版遗传算法解决TSP问题源码+项目说明.zip 该资源内项目源码是个人的毕设,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到94.5分,放心下载使用! 该资源适合计算机相关专业(如人工智能、通信工程、自动化、软件工程等)的在校学生、老师或者企业员工下载,适合小白学习或者实际项目借鉴参考! 当然也可作为毕业设计、课程设计、课程作业、项目初期立项演示等。如果基础还行,可以在此代码基础之上做改动以实现更多功能。 > 旅行商问题(Traveling Salesman Problem,TSP)是一个经典的组合优化问题。经典的TSP可以描述为:一个商品推销员要去若干个城市推销商品,该推销员从一个城市出发,需要经过所有城市后,回到出发地。应如何选择行进路线,以使总的行程最短。 在本文中使用的数据为来自TSPLIB的`ATT48`,即美国本土48个州首府坐标,TSPLIB的已知最优解为10628。 从图论的角度来看,该问题实质是在一个带权完全无向图中,找一个权值最小的Hamilton回路。由于该问题的可行解是所有顶点的全排列,随着顶点数的增加,会产生组合爆炸,它是一个NP完全问题。 # 解决思路 由于NPC问题至今没有得到解决,TSP问题往往是通过启发式搜索(我觉得也可以叫暴力)算法来“猜”出最优解。 使用遗传算法来解决TSP问题,主要思路如下: - 使用一个不重复的,首尾相同的字符串来表示一个解,该字符串即TSP的顺序 - 使用交叉,变异两种方式产生新的解,并根据数据来计算解的适应度 - 种群定义为当前所有解的一个集合,当种群中的每个个体完成一次“进化”(由概率决定)之后,称之为进化一代 - 对每一代种群进行筛选(根据适应度进行排序),择优劣汰,具有更优秀适应度的个体有更高的概率被其他个体选中进行交叉 - 不断重复上述过程,直到出现可以完全适应的个体(最优解) 遗传算法在TSP问题上可以融合多种算法,从而达到不同的效果,比如交叉应该如何交叉,变异应该如何变异等等。同时遗传算法的参数难以调整到最优——包括交叉率,变异率,种群容量等可以对搜索过程产生较大影响的参数都难以调整。限于个人水平,我无法从数学上给出最优参数,只能以经验论,加以多次实验选取表现优秀的样本。 # 解决方案 - 语言:C++ - 个体(解)的表示:用`vector`储存,代表节点遍历顺序 - 距离的计算:取伪欧式(pseudo Euclidean)距离,计算方法如下(向上取整): ![](assets/2.png) 使用`unordered_map`,内部实现为散列表,使得计算个体适应度可以达到*O*(N)​级别的复杂度 - 随机数的生成:设置时间种子,并由此生成随机数 - 交叉对象的选择:轮盘赌 - 交叉算法的选择:顺序移位 - 变异算法的选择:顺序移位 / 贪婪倒位 - 参数的选择:由多次实验得出 # Feature - 种群自动扩增 - 贪心初始化 - 自动调整变异算子 - 自动调整变异率 实验记录 - 实验样本:ATT48@TSPLIB(`att48.tsp`) - 理论最优解:10628 - 实验平台:CLion 2019.3(G++ 8.2.1 x64) ## 1000代 | 序号 | 最优解 | 耗时 / s | | ---- | ------- | -------- | | 0 | 12530 | 0.871 | | 1 | 11965 | 1.061 | | 2 | 12571 | 0.904 | | 3 | 11974 | 0.84 | | 4 | 12295 | 1.108 | | 5 | 12528 | 0.793 | | 6 | 12601 | 1.285 | | 7 | 12505 | 0.82 | | 8 | 11605 | 1.096 | | 9 | 12410 | 0.901 | | AVG | 12298.4 | 0.9679 | ## 5000代 | 序号 | 最优解 | 耗时 / s | | ---- | ------- | -------- | | 0 | 10906 | 3.349 | | 1 | 11122 | 3.899 | | 2 | 11265 | 4.49 | | 3 | 11591 | 5.184 | | 4 | 11407 | 6.562 |
图的深度遍历和广度遍历是图论中比较基础的算法,下面是C++实现的代码: ```cpp #include <iostream> #include <vector> #include <queue> #include <stack> using namespace std; // 定义一个无向图类 class Graph { private: int V; // 图中顶点的数量 vector<int> *adj; // 邻接表 public: Graph(int V); // 构造函数 void addEdge(int v, int w); // 添加边 void DFS(int v); // 深度优先遍历 void BFS(int v); // 广度优先遍历 }; Graph::Graph(int V) { this->V = V; adj = new vector<int>[V]; } void Graph::addEdge(int v, int w) { adj[v].push_back(w); adj[w].push_back(v); } void Graph::DFS(int v) { // 创建一个 bool 类型的数组,表示每个顶点是否被访问 bool *visited = new bool[V]; for (int i = 0; i < V; i++) { visited[i] = false; } // 创建一个栈,用于存储已经经过的顶点 stack<int> s; // 将起始顶点加入栈中 s.push(v); while (!s.empty()) { // 取出栈顶元素 v = s.top(); s.pop(); // 如果该顶点还没有被访问过,就将其标记为已访问 if (!visited[v]) { cout << v << " "; visited[v] = true; } // 遍历该顶点的所有邻接顶点 for (auto it = adj[v].begin(); it != adj[v].end(); ++it) { if (!visited[*it]) { // 如果邻接顶点还没有被访问过,就将其加入栈中 s.push(*it); } } } delete[] visited; } void Graph::BFS(int v) { // 创建一个 bool 类型的数组,表示每个顶点是否被访问 bool *visited = new bool[V]; for (int i = 0; i < V; i++) { visited[i] = false; } // 创建一个队列,用于存储已经经过的顶点 queue<int> q; // 将起始顶点加入队列中 q.push(v); while (!q.empty()) { // 取出队首元素 v = q.front(); q.pop(); // 如果该顶点还没有被访问过,就将其标记为已访问 if (!visited[v]) { cout << v << " "; visited[v] = true; } // 遍历该顶点的所有邻接顶点 for (auto it = adj[v].begin(); it != adj[v].end(); ++it) { if (!visited[*it]) { // 如果邻接顶点还没有被访问过,就将其加入队列中 q.push(*it); } } } delete[] visited; } int main() { Graph g(5); g.addEdge(0, 1); g.addEdge(0, 2); g.addEdge(1, 2); g.addEdge(1, 3); g.addEdge(2, 3); g.addEdge(3, 4); cout << "DFS: "; g.DFS(0); cout << endl; cout << "BFS: "; g.BFS(0); cout << endl; return 0; } ``` 上面的代码中,我们定义了一个 `Graph` 类来表示一个无向图,其中包含了两个成员函数 `DFS()` 和 `BFS()` 分别用于实现深度优先遍历和广度优先遍历。在 `DFS()` 和 `BFS()` 函数中,我们都创建了一个 bool 类型的数组 `visited`,用于表示每个顶点是否被访问过。然后分别使用栈和队列来存储已经经过的顶点,从而实现遍历整个图的过程。 最后我们在 `main()` 函数中创建了一个 `Graph` 对象 `g`,并添加了一些边。然后分别调用 `DFS()` 和 `BFS()` 函数来遍历整个图。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值