数据结构:图

首先,得了解一些图的基础概念,才能在往下走的时候不出现误解;
图:由边顶点集(vertex)和边集组成(edge)
例子:
在这里插入图片描述
在这里插入图片描述
所以又分为有向图和无向图;
图的存储结构一般有邻接矩阵和邻接表,在做笔试题的时候,领接矩阵一般会内存冗余度大,所以一般选用邻接表。
邻接表:
在这里插入图片描述
在这里插入图片描述
GraphAdjList
这里是邻接表的实现;
我们可以用C++中的unorder_map进行实现,只需要边表节点VertexNode集合。
在这里插入图片描述
首先,将顶点集正的节点都new内存空间保存到Graph中,可以看到,索引key就是顶点的值,value包含first是索引的值,second就是节点的指针。初始化的时候weight为0,next为NULL,只传入了data。
2、算法部分
(1)深度遍历图(DFS)
关键点:
①引入一个数组visited,用于保存各个结点的状态,是否已经被访问?
(2)广度 遍历就是在加visited的基础上,再加一个队列,类似于树的层序遍历
(3)图的Dijkstra最短路径算法
关键点:
①需要构建一个图,最好用邻接表
②需要一个类似于visited的finished的一个表
③注意需要一个起点,并且是计算这个起点到所有其他节点的最短距离,注意是最短距离,用数组D来记录各个结点到起点的最短距离,用数组P来表示各个几点到起点的最短距离情况下的上一个节点,这样在算法完成之后,就可以利用数组P找到该图中任一结点与起点的路径。
④核心部分:首先得初始化D,和P,其中D的最重要的,与起点无直接连接的先初始化为一个相当大的值,便于后面优化。我们需要的一个外循环表示总共需要的次数是图中节点的长度-1(除去了起点本身),外循环中有两个内循环,第一个内循环就是在D中找到最小值(也就是与起点距离最小的)并且该值得索引在finished中是0,也就是没有处理过的,原因是每一次我们找到最小值及其索引后,会在接下来的并行的第二个循环中取遍历该下标对应的节点的相邻且没有没访问的节点,通过一个比较来更新D和P,索引在每次更新后D中的最小值会发生变化而且finished也会发生变化,使得最终进行下去,最终finished数组中的值会是全为1;
⑤注意:D和P是对应的,所以在刚开始初始化时,因为D中的元素值都是到start的距离,所以P中所有的值应该为start。例如:
P=[start,start,start,start,start,…]
D=[0,1,3,inf,inf,3,4,inf,inf,inf,…]

// Graph1_1.cpp : 定义控制台应用程序的入口点。
//


#include "stdafx.h"
#include<iostream>
#include<map>
#include<unordered_map>
#include<queue>
#include<list>
using namespace std;
typedef struct VertexNode {
	int data;
	double weight;
	VertexNode* next;
	VertexNode() :data(0), weight(0), next(NULL) {}
	VertexNode(int _data) :data(_data), weight(0), next(NULL) {}
	VertexNode(int data_, double weight_) :data(data_), weight(weight_), next(NULL) {}
	VertexNode(VertexNode* VertexNode_, int weight_) {
		data = VertexNode_->data;
		weight = weight_;
		next = NULL;
	}
};
void DFS(unordered_map<int, VertexNode*>& Graph, int i, bool* visited) {
	visited[i] = true;//标记成已访问
	VertexNode* p = Graph[i];
	cout << "节点:" << i << endl;
	while (p->next)
	{
		p = p->next;
		if (!visited[p->data])
			DFS(Graph, p->data, visited);
	}
}
void Dijkstra(unordered_map<int, VertexNode*>& Graph, int* P, int* D, int start);

int main()
{
	unordered_map<int, VertexNode*> Graph;//用unordered_map实现hash_table,用于构造图结构
	cout << "初始化G的篮子个数为:" << Graph.bucket_count() << endl
		<< "第一个篮子大小:" << Graph.bucket(0) << endl;
	int V[] = { 0,1,2,3 };//顶点集
	int E[][2]{ { 0,1 },{ 0,3 },{ 0,2 },{ 1,2 },{ 2,3 } };//边集
	int nums = 4;
	cout << "开始构图:" << endl;
	for (int i = 0; i < 4; i++) {
		Graph[V[i]] = new  VertexNode(V[i]);//创建节点
		cout << "第" << i << "个节点创建成功,并保存到图中。" << endl;
	}
	cout << "开始将边加入:" << endl;
	for (int i = 0; i < 5; i++) {
		VertexNode* temp1 = Graph[E[i][0]];
		VertexNode* temp2 = Graph[E[i][1]];
		while (temp1->next)
			temp1 = temp1->next;
		temp1->next = new VertexNode(Graph[E[i][1]], 1);
		while (temp2->next)
			temp2 = temp2->next;
		temp2->next = new VertexNode(Graph[E[i][0]], 1);
	}
	//接下来介绍DFS,深度遍历图算法,关键是引入一个标记,用于检测该该点是否访问?
	//深度遍历有点像树中的前序遍历
	cout << "开始进行深度遍历:" << endl;
	bool visited[4];
	for (int i = 0; i < nums; i++)
		visited[i] = false;//初始化全为未访问
	for (int i = 0; i < Graph.size(); i++)
	{
		if (!visited[i]) {
			DFS(Graph, i, visited);
		}
	}
	//接下来进行BFS,广度遍历图算法,关键就是用一个队列进行推进,有点想树中的层序遍历。
	cout << "进行广度遍历:" << endl;
	for (int i = 0; i < nums; i++)
		visited[i] = false;//初始化全为未访问
	queue<VertexNode*> Q;
	Q.push(Graph[0]);//将图中第一个放入队列中
	while (!Q.empty() && !visited[Q.front()->data])//可能会重复添加,所以加条件去掉
	{
		auto p = Q.front();
		visited[p->data] = true;
		cout << "节点:" << p->data << endl;
		while (p->next)
		{
			p = p->next;
			if (!visited[p->data])
				Q.push(Graph[p->data]);
		}
		Q.pop();
	}
	//接下来进行最短路径了
	//关键是需要两个记录的数组,P(prenode)记录前一个节点索引,D(distance)当前最小的距离
	//还有一个关键类似于记录起点开始已经深度遍历下的点,visited[],还是有所区别的,慢慢体会。
	int* P = new int[Graph.size()]();//int加()可以初始化为0
	int* D = new int[Graph.size()]();
	for (int i = 0; i < Graph.size(); i++)
		D[i] = 1;
	Dijkstra(Graph, P, D, 0);
	for (int i = 0; i < 4; i++)
		cout << P[i] << ":" << D[i] << endl;
	return 0;
}

void Dijkstra(unordered_map<int, VertexNode*>& Graph, int* P, int* D, int start)
{
	bool* finished = new bool[Graph.size()]();
	finished[start] = true;
	P[start] = 0;
	D[start] = 0;
	int m;
	for (int i = 0; i < Graph.size(); i++)
	{
		int min = 9999;
		for (int i = 1; i < Graph.size(); i++) {
			if (!finished[i] && D[i] < min) {
				min = D[i];
				m = i;
			}
		}
		finished[m] = true;
		auto p = Graph[m]->next;
		while (p)
		{
			if (!finished[p->data] && D[p->data] > D[m] + p->weight)
			{
				D[p->data] = D[m] + p->weight;
				P[p->data] = m;
			}
			p = p->next;
		}

	}
	for (int i = 0; i<Graph.size(); i++)
		cout << finished[i] << " ";
	cout << endl;
}


补充:关于类似九宫格的最短路径问题
其实和图中的Dijkstra最短路径算法一样,只不过大体思路是一样的,finished标记,P 记录前一个节点,D记录距离起点的距离,不过这些都是用二维数组来表示对的,
基本的流程是一样的,首先初始化P和D,P中全为指向起点的值,标记finished,用hi+j来表示,解调就是i= val/h,j = val%h;D 初始化为最大值,除了在起点附近的点(且满足 一些条件(示题目而变)),然后就是找最小值以及对应的下标(i,j)所以可以用两个循环来求,注意这里比较的的D中的元素,而G九宫格中的数值只是条件(比如长度),其后就是将得到的下标finished标记为1,最后就是找下标的邻接下标,并且更新D和P(注意不要动finish),依次循环下去(终止条件在不优化的时候就是节点的长度,也就是wh,这个与图的一致,图也是以所有节点的数量作为终止条件)。

#include "stdafx.h"
#include<iostream>
#include<map>
#include<string>
#include<vector>
using namespace std;
void findword(string str, map<int, string>& D, vector<string> word)
{
	vector<string> word1(word);
	int key = 0;
	for (int i = 0; i < str.size(); i++)
	{
		key = int(str[i])-int('0');
		auto iter = word1.begin();
		while (iter != word1.end())
		{
			if (iter->size() < i + 1 || ((*iter)[i] < D[key][0]
				|| (*iter)[i] > D[key][1]))
				iter = word1.erase(iter);
			else
				iter++;
		}
	}
	cout<<word1[0]<<endl;
}

int main()
{
	int n;
	cin >> n;
	string temp;
	map<int, string> D;
	D[2] = "ac"; D[3] = "df"; D[4] = "gi"; D[5] = "jl";
	D[6] = "mo"; D[7] = "pq"; D[8] = "tv"; D[9] = "wz";
	vector<string> word(n);
	//vector<int> data;
	for (int i = 0; i < n; i++)
		cin >> word[i];
	cin >> temp;
	findword(temp, D, word);
	return 0;
}

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页