图论与算法(遍历、最小生成树、最短路径)

这篇具有很好参考价值的文章主要介绍了图论与算法(遍历、最小生成树、最短路径)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

图的基本概念

图是由顶点集合及顶点间的关系组成的一种数据结构:G = (V, E),其中:顶点集合V = {x|x属于某个数据对象集}是有穷非空集合;E = {(x,y)|x,y属于V}或者E = {<x, y>|x,y属于V && Path(x, y)}是顶点间关系的有穷集合,也叫做边的集合。(x, y)表示x到y的一条双向通路,即(x, y)是无方向的;Path(x, y)表示从x到y的一条单向通路,即Path(x, y)是有方向的。
顶点和边:图中结点称为顶点,第i个顶点记作vi。两个顶点vi和vj相关联称作顶点vi和顶点vj之间
有一条边,图中的第k条边记作ek,ek = (vi,vj)或<vi,vj>。
有向图和无向图:在有向图中,顶点对<x, y>是有序的,顶点对<x,y>称为顶点x到顶点y的一条
边(弧),<x, y>和<y, x>是两条不同的边。在无向图中,顶点对(x, y)是无序的,顶点对(x,y)称为顶点x和顶点y相关联的一条边,这条边没有特定方向,(x, y)和(y,x)是同一条边。注意:无向边(x, y)等于有向边<x, y>和<y, x>。

图论与算法(遍历、最小生成树、最短路径),算法,图论,深度优先,广度优先,迭代加深,图搜索算法

完全图:在有n个顶点的无向图中,若有n * (n-1)/2条边,即任意两个顶点之间有且仅有一条边,
则称此图为无向完全图,比如上图G1;在n个顶点的有向图中,若有n * (n-1)条边,即任意两个
顶点之间有且仅有方向相反的边,则称此图为有向完全图。
邻接顶点:在无向图中G中,若(u, v)是E(G)中的一条边,则称u和v互为邻接顶点,并称边(u,v)依
附于顶点u和v;在有向图G中,若<u, v>是E(G)中的一条边,则称顶点u邻接到v,顶点v邻接自顶
点u,并称边<u, v>与顶点u和顶点v相关联。
顶点的度:顶点v的度是指与它相关联的边的条数,记作deg(v)。在有向图中,顶点的度等于该顶
点的入度与出度之和,其中顶点v的入度是以v为终点的有向边的条数,记作indev(v);顶点v的出度
是以v为起始点的有向边的条数,记作outdev(v)。因此:dev(v) = indev(v) + outdev(v)。注意:对于无向图,顶点的度等于该顶点的入度和出度,即dev(v) = indev(v) = outdev(v)。
路径:在图G = (V, E)中,若从顶点vi出发有一组边使其可到达顶点vj,则称顶点vi到顶点vj的顶
点序列为从顶点vi到顶点vj的路径。
路径长度:对于不带权的图,一条路径的路径长度是指该路径上的边的条数;对于带权的图,一
条路径的路径长度是指该路径上各个边权值的总和。

图论与算法(遍历、最小生成树、最短路径),算法,图论,深度优先,广度优先,迭代加深,图搜索算法

简单路径与回路:若路径上各顶点v1,v2,v3,…,vm均不重复,则称这样的路径为简单路
径。若路径上第一个顶点v1和最后一个顶点vm重合,则称这样的路径为回路或环。

图论与算法(遍历、最小生成树、最短路径),算法,图论,深度优先,广度优先,迭代加深,图搜索算法

连通图:在无向图中,若从顶点v1到顶点v2有路径,则称顶点v1与顶点v2是连通的。如果图中任
意一对顶点都是连通的,则称此图为连通图。
强连通图:在有向图中,若在每一对顶点vi和vj之间都存在一条从vi到vj的路径,也存在一条从vj到
vi的路径,则称此图是强连通图。
生成树:一个连通图的最小连通子图称作该图的生成树。有n个顶点的连通图的生成树有n个顶点
和n-1条边。

图的存储结构

图的存储分为邻接矩阵和邻接表(不讲)。图因为节点与节点之间的关系就是连通与否,即为0或者1,因此邻接矩阵(二维数组)即是:先用一个数组将定点保存,然后采用矩阵来表示节点与节点之间的关系。

//K:表示存储的节点名称的数据类型,W:表示图中边的权重或者距离的数据类型,
// MAX_W:表示俩点之间不相连的象征,direction:表示方向,判断该图是否有向
template<class K, class W, W MAX_W = INT_MAX, bool direction = false>
class graph
{
	typedef graph<K, W, MAX_W, direction> Self;
public:
	//构造函数
	graph() = default;
	graph(const K* arr, const size_t n)//构造函数初始化
		:_vertex(n)
		, _matrix(n, vector<W>(n))
	{
		for (size_t i = 0; i < n; i++)
		{
			//完成节点下标映射关系
			_vertex[i] = arr[i];
			_index[_vertex[i]] = i;
		}

		for (size_t i = 0; i < n; i++)
		{
			for (size_t j = 0; j < n; j++)
			{
				if (i == j) _matrix[i][j] = 0;//_matrix[i][j]:表示的是从i->j的权值是多少,一般来说,如果i==j,说明i->i=0的权值,特殊情况特殊处理
				else _matrix[i][j] = MAX_W;//将还未相连接的节点之间关系全部表示无穷大,以此表示i,j不相连
			}
		}
	}
	//插入边
	void AddEdge(const K& kval1, const K& kval2, const W& val)
	{
		if (_index.find(kval1) == _index.end() || _index.find(kval2) == _index.end()) return;//简单处理,检查构成边的俩节点是否都在图中。

		int i1 = _index[kval1], i2 = _index[kval2];//获取节点的对应下标
		_AddEdge(i1, i2, val);//通过下标访问矩阵,同时建造俩节点关系权值为val。
	}
private:
	void _AddEdge(const int i1, const int i2, const W& val)
	{
		_matrix[i1][i2] = val;//构造俩节点关系
		if (direction == false) _matrix[i2][i1] = val;//判断是否为无向图,因为无向图i1->i2的同时i2->i1
	}
private:
	vector<K> _vertex;//通过将节点存储在数组中,这样可以用数组下标映射节点。
	unordered_map<K, int>  _index;//存储节点对应的下标映射关系。方便通过节点名称从而直接得出对应的映射关系,方便矩阵的操作等
	vector<vector<W>> _matrix;//通过节点映射下标,存储节点之间的关系。
};

图的遍历

广度遍历

图论与算法(遍历、最小生成树、最短路径),算法,图论,深度优先,广度优先,迭代加深,图搜索算法

图的广度遍历从出发点开始:不断搜寻与出发点第N等级近的关系的节点。例如:A与B、C、D是第一等级近的节点,E、F是第二等级近的节点,G、H是第三等级近的节点,I是第四等级近的节点。

//广度遍历
void BFS(const K& kval)
{
	if (_index.find(kval) == _index.end()) return;//简单处理,查看起始点是否在图中

	int i = _index[kval];//获取节点下表
	queue<int> que;//构建一个队列,以此可以存储节点
	int n = _index.size();
	vector<bool> isvisited(n, false);//构建数组,记忆是否已经遍历对应节点
	
	que.push(i);//传入起始点
	isvisited[i] = true;//传入的同时,需要将节点记忆为已经遍历
	int count = 1;//表示的是第n等级近亲的成员共有几个->现在是第0等级,count为1表示的是当前起始点
	while (!que.empty())
	{
		for (size_t i = 0; i < count; i++)//开始每一层逐层遍历
		{
			int front = que.front();
			que.pop();
			cout << _vertex[front] << " ";
			for (size_t i = 0; i < n; i++)
			{
				if (isvisited[i]==true || _matrix[front][i] == 0 || _matrix[front][i] == MAX_W) continue;//对于已经遍历的节点或者俩节点不相连的或节点合一的都跳过

				que.push(i);//传入下一层的节点
				isvisited[i] = true;//将传入队列的节点置为已经遍历,防止将已经遍历的节点重复录入队列
			}
		}
		count = que.size();//获取下一层节点个数
		cout << endl;
	}
}

深度遍历

图论与算法(遍历、最小生成树、最短路径),算法,图论,深度优先,广度优先,迭代加深,图搜索算法

图的深度优先搜索从图中的一个顶点出发,沿着一条路径尽可能深地搜索,直到不能再继续为止,然后回溯并尝试探索其他路径。在深度优先搜索中,算法会递归地访问每个顶点,并标记已经访问过的顶点,以避免重复访问。

//深度遍历
void DFS(const K& kval)
{
	if (_index.find(kval) == _index.end()) return;//简单处理,查看起始点是否在图中

	int i = _index[kval];//获取节点下表
	int n = _index.size();
	vector<bool> isvisited(n, false);//构建数组,记忆是否已经遍历对应节点

	_DFS(i, isvisited,n);//进入递归函数
}

//深度遍历函数调用
void _DFS(const int i, vector<bool>& isvisited,int n)
{
	cout << _vertex[i] << " ";//每进入一次该函数,就说明当前节点就是要遍历的节点
	isvisited[i] = true;

	for (size_t j = 0; j < n; j++)
	{
		if (isvisited[j] == true || _matrix[i][j] == 0 || _matrix[i][j] == MAX_W) continue;

		_DFS(j, isvisited, n);
	}
}

图的最小生成树

连通图中的每一棵生成树,都是原图的一个极大无环子图,即:从其中删去任何一条边,生成树
就不在连通;反之,在其中引入任何一条新边,都会形成一条回路。
若连通图由n个顶点组成,则其生成树必含n个顶点和n-1条边。因此构造最小生成树的准则有三
条:
1. 只能使用图中的边来构造最小生成树
2. 只能使用恰好n-1条边来连接图中的n个顶点
3. 选用的n-1条边不能构成回路
构造最小生成树的方法:Kruskal算法和Prim算法。这两个算法都采用了逐步求解的贪心策略。
贪心算法:是指在问题求解时,总是做出当前看起来最好的选择。也就是说贪心算法做出的不是
整体最优的的选择,而是某种意义上的局部最优解。贪心算法不是对所有的问题都能得到整体最优
解。

Kruskal算法

Kruskal算法是一种全局贪心算法,他的操作是不断选取图的当前还未选取的边的权值最小的边同时确保当前所选的边不能构成环,直到选出n-1条边。

//最小生成树—Kruskal
//typedef graph<K, W, MAX_W, direction> Self;
W Kruskal(Self& mintree)
{
	//初始化新的图,将最小生成树在mintree中存储
	int n = _index.size();
	//节点以及映射关系不变,可以直接赋值
	mintree._index = _index;
	mintree._vertex = _vertex;
	//需要将矩阵初始化为最初状态,才好填入最小生成树构造的关系
	mintree._matrix = vector<vector<W>>(n, vector<W>(n));
	for (size_t i = 0; i < n; i++)
	{
		for (size_t j = 0; j < n; j++)
		{
			if (i == j) mintree._matrix[i][j] = 0;
			else mintree._matrix[i][j] = MAX_W;
		}
	}

	std::priority_queue<Edge, vector<Edge>, std::greater<Edge>> q;//用优先级队列选取还未选取的最小权值的边

	for (size_t i = 0; i < n; i++)
	{
		for (size_t j = 0; j < n&&j<i; j++)
		{
			if (_matrix[i][j] == 0 || _matrix[i][j] == MAX_W) continue;

			q.push(Edge(i, j, _matrix[i][j]));//将当前图的全部的边录入优先级队列
		}
	}
	FindUnionSet set(n);//使用并查集,来检测俩节点的连接是否会出现环
	W result=W();//记录成功构建最小生成树的权值总和
	int size = 0;//来检测是否生成了最小生成树,因为当迭代结束后依旧未能选取n-1条边获取超出n-1条边时,就形成不了最小生成树
	while (!q.empty())
	{
		Edge e = q.top();
		q.pop();
		if (set.IsSet(e._srci,e._dsti)==false)//判断俩节点的连接是否会出现环
		{
			set.Union(e._srci, e._dsti);//如果不成环,那么就是将节点装入并查集,以便下次判断

			mintree._AddEdge(e._srci, e._dsti, e._w);//将选取的边添加到mintree中
			size++;
			result += e._w;
		}
	}
	if (size == n - 1) return result;
	return W();
}

Prim算法

Prim算法的核心思想是每次选择与当前最小生成树相邻的权重最小的边,以逐步扩展生成最小生成树。算法保证了每一步选择的边都是当前生成树中与其他顶点相邻的最短边,从而最终得到最小生成树。

Prim算法的基本思想是从一个起始顶点开始,逐步扩展生成最小生成树。具体步骤如下:
选择一个起始顶点作为初始树,将该顶点加入最小生成树中。
重复以下步骤,直到最小生成树包含所有顶点:(1)在当前最小生成树中找到与树相邻的顶点中权重最小的边,将该边加入最小生成树。(2)将新加入的顶点也加入最小生成树。

//最小生成树—Prim
//typedef graph<K, W, MAX_W, direction> Self;
W  Prim(Self& mintree,const K& kval)
{
	//初始化新的图,将最小生成树在mintree中存储
	int n = _index.size();
	//节点以及映射关系不变,可以直接赋值
	mintree._index = _index;
	mintree._vertex = _vertex;
	//需要将矩阵初始化为最初状态,才好填入最小生成树构造的关系
	mintree._matrix = vector<vector<W>>(n, vector<W>(n));
	for (size_t i = 0; i < n; i++)
	{
		for (size_t j = 0; j < n; j++)
		{
			if (i == j) mintree._matrix[i][j] = 0;
			else mintree._matrix[i][j] = MAX_W;
		}
	}
	int i = _index[kval];

	vector<bool> isvisited(n, false);//构造数组,判断是否已经连接,因为对于Prim算法来说,他每一次只会将一个已经录入最小生成树的节点和一个未录入最小生成树的节点构造成的边进行添加
	std::priority_queue<Edge, vector<Edge>, std::greater<Edge>> q;//构建优先级队列,将已经遍历的节点的相邻的边存入进行挑选
	isvisited[i] = true;
	W totalW = W();

	for (size_t j = 0; j < n; j++)
	{
		if (_matrix[i][j] != 0 && _matrix[i][j] != MAX_W)
		{
			q.push(Edge(i, j, _matrix[i][j]));//将出发点相邻的边存入优先级队列
		}
	}
	size_t size = 0;
	while (!q.empty())
	{
		Edge min = q.top();
		q.pop();
		if (isvisited[min._dsti] == false)//不断从优先级队列中取出权重最小的边,将其目标顶点加入最小生成树,并更新总权重。直到最小生成树包含所有顶点或者所有边都被考虑过。
		{
			isvisited[min._dsti] = true;
			mintree._AddEdge(min._srci, min._dsti, min._w);
			size++;
			totalW += min._w;
			if (size == n - 1) break;


			for (size_t j = 0; j < n; j++)
			{
				if (_matrix[min._dsti][j] != 0 && _matrix[min._dsti][j] != MAX_W && isvisited[j] == false)
				{
					q.push(Edge(min._dsti, j, _matrix[min._dsti][j]));
				}
			}
		}
	}
	if (size == n - 1) return totalW;
	return W();
}

图的最短路径

单源最短路径--Dijkstra算法

单源最短路径问题:给定一个图G = ( V , E ) G=(V,E)G=(V,E),求源结点s ∈ V s∈Vs∈V到图中每个结点v ∈ V v∈Vv∈V的最短路径。Dijkstra算法就适用于解决带权重的有向图上的单源最短路径问题,同时算法要求图中所有边的权重非负。一般在求解最短路径的时候都是已知一个起点
和一个终点,所以使用Dijkstra算法求解过后也就得到了所需起点到终点的最短路径。
针对一个带权有向图G,将所有结点分为两组S和Q,S是已经确定最短路径的结点集合,在初始时
为空(初始时就可以将源节点s放入,毕竟源节点到自己的代价是0),Q 为其余未确定最短路径
的结点集合,每次从Q 中找出一个起点到该结点代价最小的结点u ,将u 从Q 中移出,并放入S 
中,对u 的每一个相邻结点v 进行松弛操作。松弛即对每一个相邻结点v ,判断源节点s到结点u 
的代价与u 到v 的代价之和是否比原来s 到v 的代价更小,若代价比原来小则要将s 到v 的代价更新
为s 到u 与u 到v 的代价之和,否则维持原样。如此一直循环直至集合Q 为空,即所有节点都已经
查找过一遍并确定了最短路径,至于一些起点到达不了的结点在算法循环后其代价仍为初始设定
的值,不发生变化。Dijkstra算法每次都是选择V-S中最小的路径节点来进行更新,并加入S中,所
以该算法使用的是贪心策略。
Dijkstra算法存在的问题是不支持图中带负权路径,如果带有负权路径,则可能会找不到一些路
径的最短路径。

图论与算法(遍历、最小生成树、最短路径),算法,图论,深度优先,广度优先,迭代加深,图搜索算法

//最短路径—Dijkstra
void Dijkstra(const K& kval, vector<W>& dist, vector<int>& pPath)
{
	//初始化
	int n = _index.size(), srci = _index[kval];
	dist.resize(n, MAX_W);
	pPath.resize(n, -1);
	pPath[srci] = srci, dist[srci] = 0;

	vector<bool> shortpath(n,false);
	for (size_t j = 0; j < n; j++)
	{
		//查找目前确认的最短的路径的点
		int u = 0;
		W min = MAX_W;
		for (size_t k = 0; k < n; k++)
		{
			if (shortpath[k] == false && dist[k]<min)
			{
				min = dist[k];
				u = k;
			}
		}
		shortpath[u] = true;

		//进行比较:dist[u]+_matrix[u][v]<dist[v];

		for (size_t v = 0; v < n; v++)
		{
			if (shortpath[v] == false && _matrix[u][v] != 0 && _matrix[u][v] != MAX_W && dist[u] + _matrix[u][v] < dist[v])
			{
				dist[v] = dist[u] + _matrix[u][v];
				pPath[v] = u;
			}
		}
	}
}

结合案例分析Dijkstra算法的操作步骤

图论与算法(遍历、最小生成树、最短路径),算法,图论,深度优先,广度优先,迭代加深,图搜索算法

结合代码和案例,分析为什么Dijkstra算法不能计算带有负权的图的最短路径

图论与算法(遍历、最小生成树、最短路径),算法,图论,深度优先,广度优先,迭代加深,图搜索算法

单源最短路径--Bellman-Ford算法

Bellman-Ford算法是一种效率低,耗费时间长的暴力解决最短路径的算法,他是通过暴力枚举边的权值来达到算出最小路径。因此他可以进行负权路径的计算

图论与算法(遍历、最小生成树、最短路径),算法,图论,深度优先,广度优先,迭代加深,图搜索算法

//最短路径—BellmanFord
bool BellmanFord(const K& kval, vector<W>& dist, vector<int>& pPath)
{
	int srci = _index[kval],n=_index.size();
	dist.resize(n, MAX_W);
	pPath.resize(n, -1);
	pPath[srci] = srci, dist[srci] = 0;

	for (size_t k = 0; k < n; k++)
	{
		int flag = false;
		for (size_t i = 0; i < n; i++)
		{
			for (size_t j = 0; j < n; j++)
			{
				if (_matrix[i][j] != 0 && _matrix[i][j] != MAX_W && dist[i] + _matrix[i][j] < dist[j])
				{
					dist[j] = dist[i] + _matrix[i][j];
					pPath[j] = i;
					flag = true;
				}
			}
		}
		if (flag == false) break;
	}

	// 还能更新就是带负权回路--带有负权回路的图的权值可以无限缩小,无法测出
	for (size_t i = 0; i < n; ++i)
	{
		for (size_t j = 0; j < n; ++j)
		{
			// srci -> i + i ->j
			if (_matrix[i][j] != MAX_W && dist[i] + _matrix[i][j] < dist[j])
			{
				return false;
			}
		}
	}

	return true;
}

在Bellman-Ford算法中,使用三层循环是为了遍历所有可能的边,并更新每个顶点到源顶点的最短距离。下面是对三层循环的作用解释:
外层循环 for (size_t k = 0; k < n; k++):这一层循环控制着迭代的次数,每次迭代都尝试通过当前已知的最短路径来更新其他顶点到源顶点的最短距离。在最坏情况下,需要进行 n−1 次迭代,因为最短路径不会超过 n−1 条边。
第二层循环 for (size_t i = 0; i < n; i++):这一层循环遍历所有顶点,作为当前最短路径的起点,尝试通过每个顶点来更新其他顶点的最短距离。
第三层循环 for (size_t j = 0; j < n; j++):这一层循环遍历所有顶点,作为当前最短路径的终点,检查是否存在从起点到终点的更短路径。如果存在更短路径,则更新终点的最短距离和前驱节点。
通过这三层循环的嵌套,Bellman-Ford算法能够在O(V⋅E) 的时间复杂度内找到单源最短路径。其中,V 表示顶点数,E 表示边数。三层循环的作用是确保每条边都被考虑到,并且在每次迭代中更新所有顶点的最短距离,直到不再有顶点的最短距离被更新为止。

多源最短路径--Floyd-Warshall算法

Floyd-Warshall算法的特点是先将直接边的权重填入距离矩阵,并将直接边的起点作为路径矩阵的值,然后判断在i->j中是否存在节点或者节点{K},使得i->{K}+{K}->j < i->j的权值,如果有,那就更新。

//最短路径—-FloydWarshall
void FloydWarshall(vector<vector<W>>& vvdist, vector<vector<int>>& vvpath)
{
	//初始化
	int n = _index.size();
	vvdist = vector<vector<W>>(n, vector<W>(n, MAX_W));
	vvpath = vector<vector<int>>(n, vector<int>(n, -1));

	//初始填表
	for (size_t i = 0; i < n; i++)
	{
		for (size_t j = 0; j < n; j++)
		{
			if (_matrix[i][j] == MAX_W) continue;

			vvdist[i][j] = _matrix[i][j];
			vvpath[i][j] = i;
		}
	}

	//动态规划开始填表
	for (size_t k = 0; k < n; k++)
	{
		for (size_t i = 0; i < n; i++)
		{
			for (size_t j = 0; j < n; j++)
			{
				if (vvdist[i][k] != MAX_W && vvdist[k][j] != MAX_W && vvdist[i][k] + vvdist[k][j] < vvdist[i][j])
				{
					vvdist[i][j] = vvdist[i][k] + vvdist[k][j];
					vvpath[i][j] = vvpath[k][j];
				}
			}
		}
	}
}

因为在i->{K}->j的节点集合{K}中最多存在n-2个节点,所以最多可能需要迭代n-2次才能完成。因此第一层循环是为了可以迭代{K}集合。文章来源地址https://www.toymoban.com/news/detail-850500.html

到了这里,关于图论与算法(遍历、最小生成树、最短路径)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 对无向图进行邻接矩阵的转化,并且利用DFS(深度优先)和BFS(广度优先)算法进行遍历输出, 在邻接矩阵存储结构上,完成最小生成树的操作。

    目录 一 实验目的 二 实验内容及要求 实验内容: 实验要求: 三 实验过程及运行结果 一 算法设计思路 二 源程序代码 三、截图 四 调试情况、设计技巧及体会 1.掌握图的相关概念。 2.掌握用邻接矩阵和邻接表的方法描述图的存储结构。 3.掌握图的深度优先搜索和广度优

    2024年02月04日
    浏览(42)
  • 图论与算法(7)最短路径问题

    最短路径问题是指在一个加权图中寻找两个顶点之间的最短路径,其中路径的长度由边的权重确定。 常见的最短路径算法包括: Dijkstra算法 :适用于解决单源最短路径问题,即从一个固定的起点到图中所有其他顶点的最短路径。该算法通过不断选择当前路径上权重最小的顶

    2024年02月06日
    浏览(31)
  • 【算法入门&图论】【模板】拓扑排序|【模板】单源最短路2 |最小生成树

    ✅作者简介:热爱后端语言的大学生,CSDN内容合伙人 ✨精品专栏:C++面向对象 🔥系列专栏:算法百炼成神 本专栏收录的均为牛客网的算法题目,内含链表、双指针、递归、动态规划、基本数据结构等算法思想的具体运用。牛客网不仅有大量的经典算法题目,也有大厂的面

    2024年02月03日
    浏览(34)
  • 数学建模十大算法04—图论算法(最短路径、最小生成树、最大流问题、二分图)

    一、最短路径问题 从图中的某个顶点出发,到达另一个顶点的 所经过的边的权重之和最小 的一条路径。 1.1 两个指定顶点之间的最短路径 问题如下:给出了一个连接若干个城镇的铁路网络,在这个网络的两个指定城镇间,求一条最短铁路线。 1.1.1 Dijkstra算法 迪杰斯特拉(D

    2024年02月02日
    浏览(48)
  • 【数据结构与算法】图的搜索——广度优先遍历、最小生成树

    邻接链表: 用字典实现.有向图的邻接链表的总长度等于图的边个数;无向图的邻接链表的总长度等于图的边个数的2倍. 邻接矩阵:用于最短路径算法. 该数据结构维护一个不相交动态集的集合,每个集合有一个代表,不关心谁做代表。 支持三种操作: MAKE_SET(x) FIND_SET(x) UNION(x,y

    2024年02月19日
    浏览(36)
  • 每天一道leetcode:1129. 颜色交替的最短路径(图论&中等&广度优先遍历)

    给定一个整数 n ,即有向图中的节点数,其中节点标记为 0 到 n - 1 。图中的每条边为红色或者蓝色,并且可能存在自环或平行边。 给定两个数组 redEdges 和 blueEdges ,其中: redEdges[i] = [ai, bi] 表示图中存在一条从节点 ai 到节点 bi 的红色有向边, blueEdges[j] = [uj, vj] 表示图中存

    2024年02月13日
    浏览(26)
  • 每天一道leetcode:433. 最小基因变化(图论&中等&广度优先遍历)

    基因序列可以表示为一条由 8 个字符组成的字符串,其中每个字符都是 \\\'A\\\' 、 \\\'C\\\' 、 \\\'G\\\' 和 \\\'T\\\' 之一。 假设我们需要调查从基因序列 start 变为 end 所发生的基因变化。一次基因变化就意味着这个基因序列中的一个字符发生了变化。 例如, \\\"AACCGGTT\\\" -- \\\"AACCGGTA\\\" 就是一次基因变化。

    2024年02月12日
    浏览(29)
  • 图论基本知识--->最短路练习--->最小生成树

    自环 重边 孤点 简单图 有向图,无向图 简单图: 无向图的度数 有向图的度数:出度,入度 每个图的最大度,最小度 完全图(无向图): 完全图(有向图): 子图,生成子图: 补图:点集相同,边集不相交,并集为完全图 连通图,连通块: 图的储存方式:邻接矩阵,邻

    2024年01月23日
    浏览(35)
  • 【C++ 实现】图论概念,最小生成树,单/多源最短路径实现

    首先节点的存取,V是节点key,vectorpairV,V map;其实已经能表达一个图了,但是这样保存节点对我们使用来说会导致复杂度高。 常用保存节点的方式,有矩阵和邻接表。 矩阵的优点:O(1) 时间找到两点是否相连以及他们的权值。 矩阵的缺点:找一点相邻的所有节点的时候是O(N

    2024年02月13日
    浏览(26)
  • 【深度优先搜索】【图论】【树】2646. 最小化旅行的价格总和

    【数位dp】【动态规划】【状态压缩】【推荐】1012. 至少有 1 位重复的数字 深度优先搜索 图论 树 现有一棵无向、无根的树,树中有 n 个节点,按从 0 到 n - 1 编号。给你一个整数 n 和一个长度为 n - 1 的二维整数数组 edges ,其中 edges[i] = [ai, bi] 表示树中节点 ai 和 bi 之间存在

    2024年02月19日
    浏览(28)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包