左程云算法基础班整理(十)(c++版本)

十.左程云算法基础班整理(十)(c++)

并查集

1.有若干个样本a、b、c、d…类型假设是V
2在并查集中一开始认为每个样本都在单独的集合里
3.用户可以在任何时候调用如下两个方法:
1.bool isSameSet(V& x,V& y):查询样本x和y是否属于同一个集合。其实就是问x和y联通没有。
2.void union(V& x,V& y):把x和y各自所在集合的所有样本合并成一个集合。把x背后所有的东西(集团)和y背后的所有东西(集团)连到一起。
4.isSameSet和union方法的代价越低越好

10.1.1isSameSet的算法逻辑

一开始是这种结构
在这里插入图片描述
同时建立一个map表
在这里插入图片描述
怎么确定两个节点是否在同一个集合?找代表点!!!如下,a往上到不能再往上,得到代表点a圈。c往上到不能再往上,得到代表点c圈。a圈和c圈不是同一个,因此,a和c不连通。
在这里插入图片描述
在这里插入图片描述
代表点一开始都是自己指向自己的,但是慢慢地代表点就不是了,可以把代表点看成根节点。。。共根节点肯定联通。没有公共根节点的肯定不连通。

10.1.2union的算法逻辑

例如我们要联通a和e,union(a,e);
在这里插入图片描述
找到a的代表点,再找到e的代表点。然后将集合内元素较少的代表点挂到较多的代表点的下面。
如果集合个数相同,谁挂谁无所谓。
小集合的头结点直接连到大集合的头结点上。
parent表是这样的:在这里插入图片描述
在这里插入图片描述
如果代表点是一个,就不用union了。

findfather是一个扁平化的操作,之前是下面那样
在这里插入图片描述
扁平化之后
在这里插入图片描述
a,b,c下面的链都不用调整,只把从x出发的直链给弄扁平。

调用频繁时,时间复杂度O(1).
不用关心怎么证,直接用结论。
以后只要是连通性的题,全都这么干。

并查集的优化有两个,一个是扁平化优化,另一个是小集合绑上大集合,也叫做秩优化。
并查集代码

#include<iostream>
#include<vector>
#include<list>
#include<stack>
#include<unordered_map>
using namespace std;

struct V
{
	int v;
};
class Node
{
public:
	int val;
	Node(V va)
	{
		val = va.v;
	}
	//bool operator!=(Node a)
	//{
		//return this!=&a;
	//}
};
class UnionSet
{
public:
	unordered_map<V, Node> nodes;
	unordered_map<Node, Node> parents;
	unordered_map<Node, int> sizeMap;

	//初始化
	UnionSet(list<V> values)
	{
		for (V cur : values)
		{
			Node node(cur);
			nodes.insert({ cur,node });
			parents.insert({ node, node });
			sizeMap.insert({ node, 1 });
		}
	}
	//从点cur开始,一直往上找,找到不能再往上的代表点,返回
	Node findFather(Node& cur)
	{
		stack<Node> path;
		while (&cur != &parents[cur])
		{
			path.push(cur);
			cur = parents.at(cur);
		}
		// cur头节点,这是一个扁平化的过程,一个优化,可以降低以后的时间复杂度
		while (!path.empty())
		{
			parents.insert({ path.top(), cur });
			path.pop();
		}
		return cur;
	}
	bool isSameSet(V& a, V& b)
	{
		if (!nodes.count(a) || !nodes.count(b))
		{
			return false;
		}
		return &findFather(nodes.at(a)) == &findFather(nodes.at(b));
	}
	void unionSet(V& a, V& b)
	{
		if (!nodes.count(a) || !nodes.count(b))
		{
			return;
		}
		Node aHead = findFather(nodes[a]);
		Node bHead = findFather(nodes[b]);

		if (&aHead != &bHead)
		{
			int aSetSize = sizeMap[aHead];
			int bSetSize = sizeMap[bHead];

			Node big = aSetSize >= bSetSize ? aHead : bHead;
			Node small = aSetSize < bSetSize ? aHead : bHead;
			parents.insert({ small,big });
			sizeMap.insert({ big, aSetSize + bSetSize });
			sizeMap.erase(small);
		}
	}
};
10.2合并用户

每个学生实例有3个id,身份证ID,B站ID,githubID。只要任意两个实例有某项id相同,都可以视为是同一个人(只要一个id相同就可以)。求问一堆实例中,一共有几个人。

这是今年的一个原题。
在这里插入图片描述

三张表,桥联,每一个新来的样本都看之前表里有没有,有的话就直接连上,数据选哪个无所谓,因为并查集会把所有的连到一块的。
不断地桥联,看最后到底有几个集合,其实就是sizemap的大小。里面放着的是最后的代表点,而一个代表点代表一个集合。
在这里插入图片描述

#include<iostream>
#include<vector>
#include<list>
#include<stack>
#include<unordered_map>
using namespace std;

struct User
{
	string a;
	string b;
	string c;
	User(string aa,string bb,string cc):a(aa),b(bb),c(cc){}
};
class Node
{
public:
	User val;
	Node(User a):val(a){}
};
class UnionSet
{
public:
	unordered_map<User, Node> nodes;
	unordered_map<Node, Node> parents;
	unordered_map<Node, int> sizeMap;

	//初始化
	UnionSet(list<User> values)
	{
		for (User cur : values)
		{
			Node node(cur);
			nodes.insert({ cur,node });
			parents.insert({ node, node });
			sizeMap.insert({ node, 1 });
		}
	}
	//从点cur开始,一直往上找,找到不能再往上的代表点,返回
	Node findFather(Node& cur)
	{
		stack<Node> path;
		while (&cur != &parents[cur])
		{
			path.push(cur);
			cur = parents.at(cur);
		}
		// cur头节点,这是一个扁平化的过程,一个优化,可以降低以后的时间复杂度
		while (!path.empty())
		{
			parents.insert({ path.top(), cur });
			path.pop();
		}
		return cur;
	}
	bool isSameSet(User& a, User& b)
	{
		if (!nodes.count(a) || !nodes.count(b))
		{
			return false;
		}
		return &findFather(nodes.at(a)) == &findFather(nodes.at(b));
	}
	void unionSet(User& a, User& b)
	{
		if (!nodes.count(a) || !nodes.count(b))
		{
			return;
		}
		Node aHead = findFather(nodes[a]);
		Node bHead = findFather(nodes[b]);

		if (&aHead != &bHead)
		{
			int aSetSize = sizeMap[aHead];
			int bSetSize = sizeMap[bHead];

			Node big = aSetSize >= bSetSize ? aHead : bHead;
			Node small = aSetSize < bSetSize ? aHead : bHead;
			parents.insert({ small,big });
			sizeMap.insert({ big, aSetSize + bSetSize });
			sizeMap.erase(small);
		}
	}
	int getSetNum()
	{
		return sizeMap.size();
	}
};
// (1,10,13) (2,10,37) (400,500,37)
// 如果两个user,a字段一样、或者b字段一样、或者c字段一样,就认为是一个人
// 请合并users,返回合并之后的用户数量
int mergeUsers(list<User> users)
{
	UnionSet unionFind(users);
	unordered_map<string, User> mapA;
	unordered_map<string, User> mapB;
	unordered_map<string, User> mapC;

	for (User user : users)
	{
		if (mapA.count(user.a))
		{
			unionFind.unionSet(user, mapA[user.a]);
		}
		else{
			mapA.insert({ user.a,user });
		}
		if (mapB.count(user.b))
		{
			unionFind.unionSet(user, mapB[user.b]);
		}
		else {
			mapB.insert({ user.b,user });
		}
		if (mapC.count(user.c))
		{
			unionFind.unionSet(user, mapC[user.c]);
		}
		else {
			mapC.insert({ user.c,user });
		}
	}
	return unionFind.getSetNum();
}

在这里插入图片描述
在这里插入图片描述

10.3图的基本性质和结构表示
10.3.1

1.由点的集合和边的集合构成
2.虽然存在有向图和无向图的概念,但是实际上都可以用有向图来表达
3.边上可能带有权值
无向图也可以看成双边有向图,所以可以看成,所有的图都是有向图。

图的结构表示主流两种方法:
1.邻接表法,2.邻接矩阵法
邻接表法:
在这里插入图片描述
邻接矩阵法:
在这里插入图片描述
图的算法本身没什么难度,最大的难度是图的表示方式比较多,不可能每个都来一遍。
下面也是一种面试时常用的图的结构
在这里插入图片描述
图的算法都不算难,只不过coding的代价比较高。
1、先用自己的最熟练的方式,实现图结构的表达
2、在自己熟悉的结构上,实现所有常用的图算法作为模板
3、把面试题提供的图结构转化为自己熟悉的图结构,再调用模板或改写即可。
在这里插入图片描述

#include<iostream>
#include<vector>
#include<unordered_set>
#include<unordered_map>

using namespace std;

//点结构的描述, A,0
struct  Node
{
	int value;//该点的值,可以是string,可以是int,也可以是其他的。
	int in;//入度,进来的边有多少
	int out;//出度,出去的边有多少,out也是next的size
	vector<Node*> next;//该节点的直接的邻居
	vector<Edge*> edges;//该节点的直接的边
	Node(int value)
	{
		this->value = value;
		in = 0;
		out = 0;
	}
};
struct Edge//这个边是有向边
{
	int weight;
	Node* from;
	Node* to;

	Edge(int weight, Node* from, Node* to)
	{
		this->weight = weight;
		this->from = from;
		this->to = to;
	}
};
struct Graph//图就是点集和边集
{
	unordered_map<int, Node*> nodes;
	unordered_set<Edge*> edges;
	Graph(){}
};

//进行转换
class GraphGenerator
{
public:
	// matrix 所有的边
	// N*3 的矩阵
	// [weight, from节点上面的值,to节点上面的值]
	//难点在于怎么转化成我们知道的那种图的结构
	Graph createGraph(vector<vector<int>>& matrix)
	{
		Graph graph;
		for (int i = 0; i < matrix.size(); i++)
		{
			// matrix[0][0], matrix[0][1]  matrix[0][2]
			int weight = matrix[i][0];
			int from = matrix[i][1];
			int to = matrix[i][2];
			if (!graph.nodes.count(from))
			{
				graph.nodes.insert(make_pair(from,new Node(from)));
			}
			if (!graph.nodes.count(to))
			{
				graph.nodes.insert({ to, new Node(to) });
			}

			Node* fromNode = graph.nodes[from];
			Node* toNode = graph.nodes[to];
			Edge* newEdge = new Edge(weight, fromNode, toNode);//创建新边
			
			fromNode->next.push_back(toNode);
			fromNode->out++;
			toNode->in++;
			fromNode->edges.push_back(newEdge);
			graph.edges.insert(newEdge);
		}
	}
};


10.4图的广(宽)度优先遍历,BFS

宽度优先遍历
1.利用队列实现
2.从源节点开始依次按照宽度入队,然后弹出
3.每弹出一个点,把该节点所有没进入队列的邻接点放入队列
4.直到队列变空

图的宽度优先遍历要建立一个unordered_set,作用是防止之前遍历过得一些节点再次遍历,防止出现环的问题

#include<iostream>
#include<queue>
#include<unordered_set>
#include<unordered_map>

using namespace std;

//点结构的描述, A,0
struct  Node
{
	int value;//该点的值,可以是string,可以是int,也可以是其他的。
	int in;//入度,进来的边有多少
	int out;//出度,出去的边有多少,out也是next的size
	vector<Node*> next;//该节点的直接的邻居
	vector<Edge*> edges;//该节点的直接的边

	Node() :value(0), in(0), out(0) {}
	Node(int value)
	{
		this->value = value;
		in = 0;
		out = 0;
	}
};
struct Edge//这个边是有向边
{
	int weight;
	Node* from;
	Node* to;

	Edge(int weight, Node* from, Node* to)
	{
		this->weight = weight;
		this->from = from;
		this->to = to;
	}
};
struct Graph//图就是点集和边集
{
	unordered_map<int, Node*> nodes;
	unordered_set<Edge*> edges;
};

class BFS 
{
public:
	void bfs(Node* node)
	{
		if (node == nullptr)
		{
			return;
		}
		queue<Node*> queue1;
		unordered_set<Node*> set1;
		queue1.push(node);
		set1.insert(node);
		while (!queue1.empty())
		{
			Node* cur = queue1.front();
			queue1.pop();
			cout << cur->value << endl;

			for (Node* next : cur->next)
			{
				if (!set1.count(next))
				{
					set1.insert(next);//set和add是同步加的
					queue1.push(next);
				}
			}
		}
	}
};
10.5图的深度优先遍历,DFS

深度优先遍历
1.利用栈实现
2.从源节点开始把节点按照深度放入栈,然后弹出
3.每弹出一个栈,把该节点下一个没有进过栈的邻接点放入栈
4.直到栈变空

深度优先遍历就是,一条路,需要走到不能再走了,再往上返回。

#include<iostream>
#include<stack>
#include<unordered_set>
#include<unordered_map>

using namespace std;

//点结构的描述, A,0
struct  Node
{
	int value;//该点的值,可以是string,可以是int,也可以是其他的。
	int in;//入度,进来的边有多少
	int out;//出度,出去的边有多少,out也是next的size
	vector<Node*> next;//该节点的直接的邻居
	vector<Edge*> edges;//该节点的直接的边

	Node() :value(0), in(0), out(0) {}
	Node(int value)
	{
		this->value = value;
		in = 0;
		out = 0;
	}
};
struct Edge//这个边是有向边
{
	int weight;
	Node* from;
	Node* to;

	Edge(int weight, Node* from, Node* to)
	{
		this->weight = weight;
		this->from = from;
		this->to = to;
	}
};
struct Graph//图就是点集和边集
{
	unordered_map<int, Node*> nodes;
	unordered_set<Edge*> edges;
};

class DFS
{
public:
	void dfs(Node* node)
	{
		if (node == nullptr)
		{
			return;
		}
		stack<Node*> stack1;
		unordered_set<Node*> set1;
		stack1.push(node);//先把头入栈
		//____________________//进栈的时候打印处理某些事情
		set1.insert(node);//标记头结点
		cout << node->value << endl;
		//___________________//
		while (!stack1.empty())
		{
			Node* cur = stack1.top();
			stack1.pop();
			for (Node* next : cur->next)
			{
				if (!set1.count(next))//如果还未入栈
				{
					//再将刚才弹出来的那个压入栈
					stack1.push(cur);
					stack1.push(next);
					//____________________//进栈的时候打印处理某些事情
					set1.insert(next);
					cout << next->value << endl;
					//___________________//
					break;//break是因为路要一条一条的走,直接回到最初栈位置
				}
			}
		}
	}
};
10.6拓扑排序

1.在图中找到所有入度为0的点输出
2.把所有入度为0的点在图中删掉,继续找入度为0的点输出,周而复始
3.图的所有点都被删除后,一次输出的顺序就是拓扑排序

要求:有向图且其中没有环
应用:事件安排,编译顺序

有环的图是没有拓扑排序的概念,无向图也不可以
所以拓扑排序一定是有向无环图。
拓扑排序可以看成依赖关系,完成一件事情的前置条件完成顺序。
最适用的场景是编译的时候。
各头文件相互包含相互依赖,其实就是一张有向无环图。
依赖包就是从最底层的东西开始编译,最后把自己的项目给编译出来。
在一张图中先找到入度为0的点,删掉该点及该点的影响,依次进行此类操作。

在这里插入图片描述

#include<iostream>
#include<vector>
#include<list>
#include<queue>
#include<unordered_set>
#include<unordered_map>

using namespace std;

//点结构的描述, A,0
struct  Node
{
	int value;//该点的值,可以是string,可以是int,也可以是其他的。
	int in;//入度,进来的边有多少
	int out;//出度,出去的边有多少,out也是next的size
	vector<Node*> next;//该节点的直接的邻居
	vector<Edge*> edges;//该节点的直接的边
	Node(int value)
	{
		this->value = value;
		in = 0;
		out = 0;
	}
};
struct Edge//这个边是有向边
{
	int weight;
	Node* from;
	Node* to;

	Edge(int weight, Node* from, Node* to)
	{
		this->weight = weight;
		this->from = from;
		this->to = to;
	}
};
struct Graph//图就是点集和边集
{
	unordered_map<int, Node*> nodes;
	unordered_set<Edge*> edges;
	Graph() {}
};
// directed graph and no loop
list<Node*> sortedTopology(Graph* graph)
{
	// key:某一个node
	// value:剩余的入度
	unordered_map<Node*, int> inMap;
	// 剩余入度为0的点,才能进这个队列
	queue<Node*> zeroInQueue;

	for (auto it = graph->nodes.begin(); it != graph->nodes.end(); it++)
	{
		Node* cur = it->second;

		inMap.insert({ cur,cur->in });//在所有点集中,先把inmap初始化一下
		if (cur->in == 0)//第一批入度为0的点,率先放到zeroinqueue里面
		{
			zeroInQueue.push(cur);
		}
	}
	// 拓扑排序的结果,依次加入result
	list<Node*> result;
	while (!zeroInQueue.empty())
	{
		Node* cur = zeroInQueue.front();
		zeroInQueue.pop();

		result.push_back(cur);

		for (Node* next : cur->next)
		{
			inMap.insert({ next, inMap[next] - 1 });//所有的邻居,入度-1;
			if (inMap[next] == 0)
			{
				zeroInQueue.push(next);
			}
		}
	}
	return result;
}
10.7克鲁斯卡尔算法(最小生成树)

1.总是从权值最小的边开始考虑,依次考察权值依次变大的边
2.当前的边要么进入最小生成树的集合,要么丢弃。
3.如果当前的边进入最小生成树的集合中不会形成环,就要当前边
4.如果当前的边进入最小生成树的集合中也会形成环,就不要当前边
5.考察完所有边之后,最小生成树的集合也就找到了。

理解并查集之后,最小生成树十分容易,有向图和无向图之间没有明显界限。
只要不破坏连通性的话,可以删掉某些边。
在这里插入图片描述
最小生成树,是在保留连通性的前提下,最省的权值组合。
在这里插入图片描述
K算法,先是对边从小到大进行排序。
之后从点集角度触发,看当前点的两侧是否为同一个连通区域,如果是同一连通区域,则此边不会加入进去。如果不是同一连通域,则该边会被加进去。
并查集做这个有天然的优势,可以查找两片区域的连通性问题。

#include<iostream>
#include<unordered_map>
#include<list>
#include<unordered_set>
#include<numeric>

using namespace std;
//点结构的描述, A,0
struct  Node
{
	int value;//该点的值,可以是string,可以是int,也可以是其他的。
	int in;//入度,进来的边有多少
	int out;//出度,出去的边有多少,out也是next的size
	vector<Node*> next;//该节点的直接的邻居
	vector<Edge*> edges;//该节点的直接的边
	Node(int value)
	{
		this->value = value;
		in = 0;
		out = 0;
	}
};
struct Edge//这个边是有向边
{
	int weight;
	Node* from;
	Node* to;

	Edge(int weight, Node* from, Node* to)
	{
		this->weight = weight;
		this->from = from;
		this->to = to;
	}
};
struct Graph//图就是点集和边集
{
	unordered_map<int, Node*> nodes;//点的集合
	unordered_set<Edge*> edges;//边的集合
	Graph() {}
};


unordered_map<Node*, list<Node*>> setMap;//第二个参数是包含第一个参数的集合
//设置集合
void mySet(list<Node*>nodes) {
	for (Node* cur : nodes) {
		list<Node*>set;
		set.push_back(cur);
		setMap[cur] = set;//当前就只将自己单独放入一个集合
	}
}
//判断是否一条边的两个端点在同一个集合
bool isSameSet(Node* from, Node* to) {
	list<Node*>fromSet = setMap[from];
	list<Node*>toSet = setMap[to];
	return fromSet == toSet;//只要在同一个集合里面,地址就相同
}
//将from所在集合和to所在集合合并在一起
void merge(Node* from, Node* to) {
	list<Node*>fromSet = setMap[from];
	list<Node*>toSet = setMap[to];
	for (Node* curTo : toSet) {
		fromSet.push_back(curTo);
		setMap.insert(make_pair(curTo, fromSet));//此时from和to所在setMap属于同一内存地址
	}
}
//生成最小树:克鲁斯卡尔算法
list<Node*> smallestTreeKruskal(Graph* graph) {
	//先将graph的边排序
	unordered_set<Edge*> edg;
	for (Edge* curEdge : graph->edges)
		edg.insert(curEdge);//ed会自动按照key值排序
	for (Edge* curEdge : edg) {
		Node* from = curEdge->from;//拿到边的两个端点
		Node* to = curEdge->to;
		if (!isSameSet(from, to)) {//不在同一容器就合并
			merge(from, to);
		}
	}
	return setMap.begin()->second;//返回点集,既有点值的信息,又有边的信息
}
10.8普利姆算法实现最小生成树

随意指定一个出发点,被解锁的点和被解锁的边。
先从一个点出发,解锁最短的边,得到新的点,再从新的点触发,解锁最短的边…依次周而复始,知道最后所有的点都被包括了进来,注意在解锁边的时候,要注意之前的结点有没有被加进来。
普利姆算法用不到并查集,因为每次都是加进来一个点,而不是两个集合合并,因此用不到并查集,一个正常的unordered_set就足够了。
在这里插入图片描述

#include<iostream>
#include<unordered_map>
#include<queue>
#include<unordered_set>

using namespace std;
//点结构的描述, A,0
struct  Node
{
	int value;//该点的值,可以是string,可以是int,也可以是其他的。
	int in;//入度,进来的边有多少
	int out;//出度,出去的边有多少,out也是next的size
	vector<Node*> next;//该节点的直接的邻居
	vector<Edge*> edges;//该节点的直接的边
	Node(int value)
	{
		this->value = value;
		in = 0;
		out = 0;
	}
};
struct Edge//这个边是有向边
{
	int weight;
	Node* from;
	Node* to;

	Edge(int weight, Node* from, Node* to)
	{
		this->weight = weight;
		this->from = from;
		this->to = to;
	}
};
struct Graph//图就是点集和边集
{
	unordered_map<int, Node*> nodes;//点的集合
	unordered_set<Edge*> edges;//边的集合
	Graph() {}
};

//比较器
class EdgeComparator {
public:
	int compare(Edge* o1, Edge* o2) {//排序方式,从小往大
		return o1->weight - o2->weight;
	}
};
unordered_set<Edge*> primMST(Graph* graph) {
	//解锁的边进入小根堆
	priority_queue<Edge*> priorityQueue;
	unordered_set<Node*> set;//标记点
	unordered_set<Edge*> result;//依次挑选的边在result里面
	//for (Node* node : graph->nodes.begin()->second) {//随便找一个点就行
	for(auto it = graph->nodes.begin();it!=graph->nodes.end();it++)
	{
		Node* node = it->second;
		//node是开始点
		if (set.find(node) == set.end()) {//如果还没遍历过该点
			set.insert(node);
			for (Edge* edge : node->edges) {//由一个点解锁所有相连的边
				priorityQueue.push(edge);
			}
			while (!priorityQueue.empty()) {
				Edge* edge = priorityQueue.top();//弹出解锁边中的最小的边
				Node* toNode = edge->to;//可能是新的点
				if (set.find(toNode) != set.end()) {//如果不含有就是新的点
					set.insert(toNode);
					result.insert(edge);//将边装进结果里面
					for (Edge* nextEdge : toNode->edges)
						priorityQueue.push(nextEdge);//将新的点的相邻的边解锁
				}
			}
		}
	}
	return result;
}
10.9路径规划(迪杰斯特拉)

要求各边权值都没有负数,返回一张最短路径表。
在这里插入图片描述在这里插入图片描述
迪杰斯特拉算法和商旅问题TSP问题是完全不一样的。迪杰斯塔拉不是全连接的。是针对某一个顶点,可以实现到达各个顶点。
商旅问题是所有节点都全连接,类似一个邮差,一定要走过所有的城市,并且回到当前的城市,每个城市值许经过一次,请问总距离如何最短。
TPS问题不指定出发点,因为是个环。而迪杰斯特拉一定要指定出发点。
迪杰斯特拉本质上是贪心思想。
迪杰斯特拉的改进:每次都遍历很多距离,选择一个最近的点。多次遍历不够快,可以使用一个小根堆来解决问题。

#include<iostream>
#include<unordered_map>
#include<queue>
#include<unordered_set>

using namespace std;
//点结构的描述, A,0
struct  Node
{
	int value;//该点的值,可以是string,可以是int,也可以是其他的。
	int in;//入度,进来的边有多少
	int out;//出度,出去的边有多少,out也是next的size
	vector<Node*> next;//该节点的直接的邻居
	vector<Edge*> edges;//该节点的直接的边
	Node(int value)
	{
		this->value = value;
		in = 0;
		out = 0;
	}
};
struct Edge//这个边是有向边
{
	int weight;
	Node* from;
	Node* to;

	Edge(int weight, Node* from, Node* to)
	{
		this->weight = weight;
		this->from = from;
		this->to = to;
	}
};
struct Graph//图就是点集和边集
{
	unordered_map<int, Node*> nodes;//点的集合
	unordered_set<Edge*> edges;//边的集合
	Graph() {}
};

unordered_map<Node*, int> dijkstral(Node* head) {
	//返回值的第一个参数:从head出发到达key,
	//返回值第二个参数:从head出发到达key的最小长度
	//如果在表中不含T,则说明从head到达T的距离为正无穷,不能到达
	unordered_map<Node*, int> distanceMap;
	distanceMap.insert(make_pair(head, 0));//自己到自己距离为0
	//锁定的结点放在selectNode中
	unordered_set<Node*>selectedNode;
	//从distanceMap中选择没有选过的最小距离的结点
	Node* minNode = geiMinDistanceAndUnselected(distanceMap, selectedNode);
	while (minNode != NULL) {
		int distance = distanceMap[minNode];
		for (Edge* edge : minNode->edges) {
			Node* toNode = edge->to;
			if (distanceMap.find(toNode) != distanceMap.end()) {
				distanceMap.insert(make_pair(toNode, distance + edge->weight));
			}
			//更新
			distanceMap.insert(make_pair(edge->to,
				min(distanceMap[toNode], distance + edge->weight)));
		}
		selectedNode.insert(minNode);
		minNode = geiMinDistanceAndUnselected(distanceMap, selectedNode);
	}
	return distanceMap;
}
//找最小路径对应的节点
Node* geiMinDistanceAndUnselected(unordered_map<Node*, int>distanceMap,
	unordered_set<Node*> selectedNode) {
	Node* minNode;
	int minValue = INT_MAX;
	for (pair<Node*, int> cur : distanceMap) {//获得对组
		Node* node = cur.first;
		int distance = cur.second;
		if (selectedNode.find(node) != selectedNode.end() && minValue > distance) {
			minNode = node;
			minValue = distance;
		}
	}
	return minNode;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值