栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

图算法入门1:基础表示方法和深度优先搜索实现

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

图算法入门1:基础表示方法和深度优先搜索实现

图结构深度优先搜索示例如下图所示:

从一个结点开始优先按照深度访问相邻节点,下面详细介绍代码实现。

图结构读取和表示

如上图的图结构,通常以文件形式进行保存,每一行第一个字符是当前节点名字,后续字符是相连接节点名字,故其存储内容如下:

s a
a s b g
g a h i
i g
h g
b a c d e
e b f
f e
d b
c b

图的表示,每一个图的节点用一个结构体表示:

typedef struct Vertex{
	string name;
	int index;
	Vertex(string inputName, int inputIndex):name(inputName),index(inputIndex){}
	Vertex():name(""),index(0){}
} VertexNode;

index对应图节点的编号索引,name对应图节点的名字,整个图结构用邻接表表示法: 

vector > adjGraph;

第一个vector的索引对应某个图节点的索引,对应内容为该图节点所有连接的图节点信息。

深度优先搜索算法

深度优先搜索算法实现:

void graphDfs(vector >& graph,  const int& here, vector& visited, const vector& indexName){
	cout << "DFS visit " << indexName[here] << endl;
	visited[here] = true;
	// 遍历所有相邻的顶点
	for(int i = 0; i < graph[here].size(); ++i) {
		int there = graph[here][i].index;
		if(!visited[there]) {
			graphDfs(graph, there, visited, indexName);
		}
	}
}

可以看到基本原理就是从一个图节点开始深度优先访问所有节点,并且一直迭代下去,直到访问完所有节点,vector visit标记是否访问过某个图节点,indexName用于保存节点编号index和名字name的转换。完整实现代码如下:

#include
#include
#include
#include
#include
#include
using namespace std;
typedef struct Vertex{
	string name;
	int index;
	Vertex(string inputName, int inputIndex):name(inputName),index(inputIndex){}
	Vertex():name(""),index(0){}
} VertexNode;
void graphDfs(vector >& graph,  const int& here, vector& visited, const vector& indexName){
	cout << "DFS visit " << indexName[here] << endl;
	visited[here] = true;
	// 遍历所有相邻的顶点
	for(int i = 0; i < graph[here].size(); ++i) {
		int there = graph[here][i].index;
		if(!visited[there]) {
			graphDfs(graph, there, visited, indexName);
		}
	}
}
int main() {
	unordered_map graphMap; // 图节点名和编号的Map
	vector > adjGraph; // 图的连接表表示法
	ifstream graphRdFile("graph_struct.txt");
	if(!graphRdFile.good()) {
		cout << "open graph file failed!" << endl;
		return -1;
	}
	string line;
	int index = 0;
	string vertexName;
	// 首先对Vertex Name进行编码
	while(graphRdFile >> vertexName) {
		if (graphMap.find(vertexName) == graphMap.end()) {
			graphMap.insert(make_pair(vertexName, index++));
		}
	}
	// 编码与Vertex的反映射
	vector indexName = vector(graphMap.size(),"");
	for(auto itr=graphMap.begin();itr!=graphMap.end();itr++) {
		indexName[itr->second] = itr->first;
	}
	// 重新读
	graphRdFile.clear();
	graphRdFile.seekg(0,std::ios::beg);
	adjGraph.resize(graphMap.size());
	int currentIndex = 0; // 当前图节点的编号
	while (getline(graphRdFile, line)) { //按行读,每一行是一个图节点的连接情况
		istringstream ss(line);
		string tmp;
		bool firstFlag = true;
		while(ss >> tmp) {
			if (firstFlag) {
				if (graphMap.find(tmp) != graphMap.end()) {
					currentIndex = graphMap[tmp];
				} else {
					break;
				}
				firstFlag = false;
				continue;
			}
			if (graphMap.find(tmp) != graphMap.end()) {
				adjGraph[currentIndex].emplace_back(VertexNode(tmp,graphMap[tmp]));
			}
		}
	}
	// 深度优先搜索测试
	vector visited = vector(graphMap.size(), false);
	graphDfs(adjGraph, 0, visited, indexName);
	return 0;
}

执行结果如下:

DFS visit s
DFS visit a
DFS visit b
DFS visit c
DFS visit d
DFS visit e
DFS visit f
DFS visit g
DFS visit h
DFS visit i

注意,这里针对的是连通图的情况,若不连通,则无法保证遍历所有图节点,例如如下图的图结构

则需要从每个节点都遍历一次,才能保证遍历所有节点:

for (int i = 0; i < adjGraph.size(); ++i) {
    if(!visited[i])
	graphDfs(adjGraph, 0, visited, indexName);
}
复杂度

graphDfs对于每个顶点调用1次,所以总共被调用次,而执行相邻边线检测的for循环,会对所有边线确认1次(有向图)或2次(无向图),故最终时间复杂度为。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/928030.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号