【数据结构】最短路径算法实现(Dijkstra(迪克斯特拉),FloydWarshall(弗洛伊德) )

文章目录

  • 前言
  • 一、Dijkstra(迪克斯特拉)
    • 1.方法:
    • 2.代码实现
  • 二、FloydWarshall(弗洛伊德)
    • 1.方法
    • 2.代码实现
  • 完整源码


前言

最短路径问题:从在带权有向图G中的某一顶点出发,找出一条通往另一顶点的最短路径,最短也就是沿路径各边的权值总和达到最小。
单源最短路径问题:给定一个图G = ( V , E ) G=(V,E)G=(V,E),求源结点s ∈ V s∈Vs∈V到图
中每个结点v ∈ V v∈Vv∈V的最短路径

一、Dijkstra(迪克斯特拉)

1.方法:

针对一个带权有向图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中,所以该算法使用的是贪心策略。

核心就是从当前选入的顶点当中去找其直接相连的最小的边,然后用这个最小边相连的另一个顶点为起点,找与其直接相连边中最小的边(eg:与s直接相连的为t,y。最小的边为5,即y顶点,其为s到y的最短距离,然后以y为起点,与y直接相连的有t,x,z。最小的边为2即z点,y到z最短为2,所以s到z最短为7,以此类推,直到所有点都被当过起点后结束)
在这里插入图片描述

2.代码实现

void Dijkstra(const V& src, vector<W>& dist, vector<int>& pPath)
		{
			//dist存的src到其他点的最短路径
			// vector<int> pPath 记录srci-其他顶点最短路径父顶点数组
			size_t srci = GetVertexIndex(src);
			size_t n = _vertexs.size();
			dist.resize(n, MAX_W);
			pPath.resize(n, -1);

			dist[srci] = 0;//自己到自己距离为0
			pPath[srci] = srci;

			// 已经确定最短路径的顶点集合
			vector<bool> S(n, false);

			for (size_t j = 0; j < n; ++j)
			{
				 
				int u = srci;//u为当前最短路径顶点
				W min = MAX_W;//min为起始点到u的距离
				for (size_t i = 0; i < n; ++i)
				{
					if (S[i] == false && dist[i] < min)
					{
						u = i;
						min = dist[i];
					}
				}


				//找到与当前起始点直接相连的最短路径的顶点后
				//将其位置置为true表明已经选入
				S[u] = true;
				// 松弛算法:更新一遍u连接的所有边,看是否能更新出更短连接路径
				for (size_t v = 0; v < n; ++v)
				{
					// 如果srci->u + u->k 比 srci->k更短 则进行更新
					if (S[v] == false && _matrix[u][v] != MAX_W
						&& dist[u] + _matrix[u][v] < dist[v])
					{
						dist[v] = dist[u] + _matrix[u][v];
						pPath[v] = u;
					}
				}
			}
		}


//打印路径
void PrintShortPath(const V& src, const vector<W>& dist, const vector<int>& pPath) {
	
			size_t srci = GetVertexIndex(src);
			size_t n = _vertexs.size();
			for (size_t i = 0; i < n; i++) {
				if (i != srci) {
					vector<int>path;
					//path为src到其他顶点路径
					size_t parenti = i;
					while (parenti != srci) {
						path.push_back(parenti);
						parenti = pPath[parenti];
					}
					path.push_back(srci);
					//需要反转一下,因为我们从s->x->v
					//是从v的父亲为x再推出x的父亲为s才结束的
					reverse(path.begin(), path.end());

					for (auto index : path) {
						cout << _vertexs[index] << "->";
					}
					cout << "权值和:" << dist[i] << endl;
				}
			}
		 }

Dijkstra算法存在的问题是不支持图中带负权路径,如果带有负权路径,则可能会找不到一些路
径的最短路径。

二、FloydWarshall(弗洛伊德)

多源最短路径:Floyd-Warshall算法是解决任意两点间的最短路径的一种算法。

1.方法

Floyd算法考虑的是一条最短路径的中间节点,即简单路径p={v1,v2,…,vn}上除v1和vn的任意节
点。
设k是p的一个中间节点,那么从i到j的最短路径p就被分成i到k和k到j的两段最短路径p1,p2。p1
是从i到k且中间节点属于{1,2,…,k-1}取得的一条最短路径。p2是从k到j且中间节点属于{1,
2,…,k-1}取得的一条最短路径。

核心将中间经过的k当成所经过s->…->j中间经过的所有中间顶点集合中的一个,把中间的所有顶点看成k。

在这里插入图片描述

在这里插入图片描述

2.代码实现

void FloydWarshall(vector<vector<W>>& vvDist, vector<vector<int>>& vvpPath)
		{
			size_t n = _vertexs.size();
			vvDist.resize(n);
			vvpPath.resize(n);

			// 初始化权值和路径矩阵
			for (size_t i = 0; i < n; ++i)
			{
				vvDist[i].resize(n, MAX_W);
				vvpPath[i].resize(n, -1);
			}
			//vvpPath[i][j]表示i->j,j的父亲为i




			// 直接相连的边更新一下
			//把目前已知直接相连的边放入vvDist中,并更新vvpPath[i][j]
			for (size_t i = 0; i < n; ++i)
			{
				for (size_t j = 0; j < n; ++j)
				{
					if (_matrix[i][j] != MAX_W)
					{
						vvDist[i][j] = _matrix[i][j];
						vvpPath[i][j] = i;
					}

					if (i == j)
					{
						vvDist[i][j] = W();
					}
				}
			}

			 
			// 最短路径的更新i-> {其他顶点} ->j
			//这里要进行k次的原因是因为我们所有结点都有可能
			//成为src与dst的中间结点,所以要考虑所有情况
			for (size_t k = 0; k < n; ++k)
			{
				for (size_t i = 0; i < n; ++i)
				{
					for (size_t j = 0; j < n; ++j)
					{
						// k 作为的中间点尝试去更新i->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];

							 

							vvpPath[i][j] = vvpPath[k][j];
							//因为这里k实际上是中间顶点集合
							// 找跟j相连的上一个邻接顶点
							// 如果k->j 直接相连,上一个点就k,vvpPath[k][j]存就是k
							// 如果k->j 没有直接相连,k->...->x->j,vvpPath[k][j]存就是x
						}
					}
				}

				// 打印权值和路径矩阵观察数据
				for (size_t i = 0; i < n; ++i)
				{
					for (size_t j = 0; j < n; ++j)
					{
						if (vvDist[i][j] == MAX_W)
						{
							//cout << "*" << " ";
							printf("%3c", '*');
						}
						else
						{
							//cout << vvDist[i][j] << " ";
							printf("%3d", vvDist[i][j]);
						}
					}
					cout << endl;
				}
				cout << endl;

				for (size_t i = 0; i < n; ++i)
				{
					for (size_t j = 0; j < n; ++j)
					{
						//cout << vvParentPath[i][j] << " ";
						printf("%3d", vvpPath[i][j]);
					}
					cout << endl;
				}
				cout << "=================================" << endl;
			}
		}
	 
	};

完整源码

如果对Graph这些代码不太熟悉的小伙伴可以参考我之前写的【数据结构】图的创建(邻接矩阵,邻接表)以及深度广度遍历(BFS,DFS)

namespace matrix {
	//V为顶点类型,W为边权值类型,MAX_W为权值最大值也就是无效值
	//Direction用来判断是不是有向图,false为无向图
	template<class V,class W,W  MAX_W=INT_MAX,bool Direction=false>
	class Graph {
	public:
		Graph() = default;
		Graph(const V* a, size_t n) {
			_vertexs.reserve(n);
			for (size_t i = 0; i < n; i++) {
				_vertexs.push_back(a[i]);
				_indexMap[a[i]] = i;
				//将顶点存入_vertexs,下标映射存进map
			}

			_matrix.resize(n);
			for (size_t i = 0; i < _matrix.size(); i++) {
				_matrix[i].resize(n, MAX_W);
				//邻接矩阵默认初始值为无效值
			}
		}

		size_t GetVertexIndex(const V& v) {
			//获得对应顶点在数组中的下标
			auto it = _indexMap.find(v);
			if (it != _indexMap.end()) {
				return it->second;
				//有这个顶点返回其下标
			}
			else {
				throw("顶点不存在");
				return -1;
			}
		}

		void _AddEdge(size_t srci, size_t dsti, const W& w) {
			//存入权值
			_matrix[srci][dsti] = w;
			if (Direction == false) {
				_matrix[dsti][srci] = w;
				//无向图要两个方向都存
			}
		}

		void AddEdge(const V& src, const V& dst, const W& w) {
			//添加边与顶点的关系。从src到dst方向的关系
			size_t srci = GetVertexIndex(src);
			size_t dsti = GetVertexIndex(dst);
			//先获取其对应的下标
			_AddEdge(srci, dsti, w);
		}

		void Print() {
			for (size_t i = 0; i < _vertexs.size(); i++) {
				cout << "[" << i << "]" << "->" << _vertexs[i] << endl;
			}//打印顶点集
			cout << endl;


			//打印邻接矩阵
			for (size_t i = 0; i < _matrix.size(); i++) {
				cout << i << " ";
				for (size_t j = 0; j < _matrix[i].size(); j++) {
					if (_matrix[i][j] == MAX_W) {
						printf("%4c", '*');
					}
					else {
						printf("%4d", _matrix[i][j]);
					}
				}
				cout << endl;
			 }
		}

	 
	 


		void BFS(const V& src) {
			size_t srci = GetVertexIndex(src);
			queue<int>q;
			q.push(srci);
			vector<bool>visited(_vertexs.size(), false);
			visited[srci] = true;//标记这个顶点被访问过了
			int levelSize = 1;
			while (!q.empty()) {
				//levelSize为当前层的大小
				for (size_t i = 0; i < levelSize; i++) {
					int front = q.front();
					q.pop();
					cout << front << ":" << _vertexs[front]<<" ";

					for (size_t i = 0; i < _vertexs.size(); i++) {
						if (_matrix[front][i] != MAX_W && visited[i] == false) {
							q.push(i);
							visited[i] = true;//标记这个顶点被访问过了
						}
					}
				}
				levelSize = q.size();//更新当前层的数量
				cout << endl;
			}
			cout << endl;
		}


		void _DFS(size_t srci, vector<bool>& visited) {
			cout << srci << ":" << _vertexs[srci] << endl;
			visited[srci] = true;//标记这个顶点被访问过了
			for (size_t i = 0; i < _vertexs.size(); i++) {
				if (_matrix[srci][i] != MAX_W && visited[i] == false) {
					_DFS(i, visited);
				}
			}
		}

		void DFS(const V& src) {
			size_t srci = GetVertexIndex(src);
			vector<bool>visited(_vertexs.size(), false);

			_DFS(srci, visited);
		}


 

	 
		 
		
		
		 

		typedef Graph<V, W, MAX_W, false> Self;

		//建立边的类,保存边的两个顶点下标和权值
		struct Edge {
			size_t _srci;
			size_t _dsti;
			W _w;

			Edge(size_t srci,size_t dsti,W w)
				:_srci(srci),
				_dsti(dsti),
				_w(w)
			{}

			bool operator>(const Edge& e)const {
				return _w > e._w;//小根堆判断
			}

		};

		W Kruskal(Self& minTree)
		{
			//minTree为最小生成树,刚开始什么都没有
			size_t n = _vertexs.size();

			//初始化最小生成树
			minTree._vertexs = _vertexs;
			minTree._indexMap = _indexMap;
			minTree._matrix.resize(n);
			for (size_t i = 0; i < n; ++i)
			{
				minTree._matrix[i].resize(n, MAX_W);
			}


			//我们每次选边从全部边中选出最小的(保证不构成回路的情况)
			//所以我们可以考虑用小根堆来存入边,这样每次方便找最小的
			priority_queue<Edge, vector<Edge>, greater<Edge>> minque;
			for (size_t i = 0; i < n; ++i)
			{
				for (size_t j = 0; j < n; ++j)
				{
					if (i < j && _matrix[i][j] != MAX_W)
					{
						//将所有有效值边放进堆中
						minque.push(Edge(i, j, _matrix[i][j]));
					}
				}
			}

			 
			int size = 0;
			W totalW = W();
			UnionFindSet ufs(n); 

			// 选出n-1条边
			while (!minque.empty())
			{
				//取出最小边
				Edge min = minque.top();
				minque.pop();

				if (!ufs.InSet(min._srci, min._dsti))//判断是否成环
				{
				
					//cout << _vertexs[min._srci] << "->" << _vertexs[min._dsti] <<":"<<min._w << endl;
					//不成环就将当前边放入最小生成树当中
					    
					minTree._AddEdge(min._srci, min._dsti, min._w);
					//并把这两个顶点放入同一个并查集集合当中
					ufs.Union(min._srci, min._dsti);
					++size;
					totalW += min._w;//权值总和增加
				}
				else
				{
					//cout << "构成环:";
					//cout << _vertexs[min._srci] << "->" << _vertexs[min._dsti] << ":" << min._w << endl;
				}
				 
			}

			if (size == n - 1)//边数选够说明最小生成树
				//创建成功
			{
				return totalW;
			}
			else
			{
				return W();
			}
		}

		W Prim(Self& minTree, const W& src)
		{
			size_t srci = GetVertexIndex(src);
			size_t n = _vertexs.size();

			minTree._vertexs = _vertexs;
			minTree._indexMap = _indexMap;
			minTree._matrix.resize(n);
			for (size_t i = 0; i < n; ++i)
			{
				minTree._matrix[i].resize(n, MAX_W);
			}

			 

			vector<bool> X(n, false);
			vector<bool> Y(n, true);
			X[srci] = true;
			Y[srci] = false;

			 
			// 从X->Y集合中连接的边里面选出最小的边
			priority_queue<Edge, vector<Edge>, greater<Edge>> minq;

			// 先把srci连接的边添加到小根堆中
			for (size_t i = 0; i < n; ++i)
			{
				if (_matrix[srci][i] != MAX_W)
				{
					minq.push(Edge(srci, i, _matrix[srci][i]));
				}
			}

			cout << "Prim开始选边" << endl;
			size_t size = 0;//选出边的数量
			W totalW = W();//权值之和
			while (!minq.empty())
			{
				Edge min = minq.top();
				minq.pop();

				// 最小边的目标点也在X集合,则构成环
				if (X[min._dsti])
				{
					//cout << "构成环:";
					//cout << _vertexs[min._srci] << "->" << _vertexs[min._dsti] << ":" << min._w << endl;
				}
				else
				{
					//从Y中选出顶点
					minTree._AddEdge(min._srci, min._dsti, min._w);//加入最小生成树
					//cout << _vertexs[min._srci] << "->" << _vertexs[min._dsti] << ":" << min._w << endl;
					X[min._dsti] = true;
					Y[min._dsti] = false;
					++size;
					totalW += min._w;
					if (size == n - 1)
						break;

					//把新加入顶点相关的边都放入小根堆中
					for (size_t i = 0; i < n; ++i)
					{
						if (_matrix[min._dsti][i] != MAX_W && Y[i])
						{
							minq.push(Edge(min._dsti, i, _matrix[min._dsti][i]));
						}
					}
				}
			}

			if (size == n - 1)
			{
				return totalW;
			}
			else
			{
				return W();
			}
		}


		void PrintShortPath(const V& src, const vector<W>& dist, const vector<int>& pPath) {
	
			size_t srci = GetVertexIndex(src);
			size_t n = _vertexs.size();
			for (size_t i = 0; i < n; i++) {
				if (i != srci) {
					vector<int>path;
					//path为src到其他顶点路径
					size_t parenti = i;
					while (parenti != srci) {
						path.push_back(parenti);
						parenti = pPath[parenti];
					}
					path.push_back(srci);
					//需要反转一下,因为我们从s->x->v
					//是从v的父亲为x再推出x的父亲为s才结束的
					reverse(path.begin(), path.end());

					for (auto index : path) {
						cout << _vertexs[index] << "->";
					}
					cout << "权值和:" << dist[i] << endl;
				}
			}
		 }



		void Dijkstra(const V& src, vector<W>& dist, vector<int>& pPath)
		{
			//dist存的src到其他点的最短路径
			// vector<int> pPath 记录srci-其他顶点最短路径父顶点数组
			size_t srci = GetVertexIndex(src);
			size_t n = _vertexs.size();
			dist.resize(n, MAX_W);
			pPath.resize(n, -1);

			dist[srci] = 0;//自己到自己距离为0
			pPath[srci] = srci;

			// 已经确定最短路径的顶点集合
			vector<bool> S(n, false);

			for (size_t j = 0; j < n; ++j)
			{
				 
				int u = srci;//u为当前最短路径顶点
				W min = MAX_W;//min为起始点到u的距离
				for (size_t i = 0; i < n; ++i)
				{
					if (S[i] == false && dist[i] < min)
					{
						u = i;
						min = dist[i];
					}
				}


				//找到与当前起始点直接相连的最短路径的顶点后
				//将其位置置为true表明已经选入
				S[u] = true;
				// 松弛算法:更新一遍u连接的所有边,看是否能更新出更短连接路径
				for (size_t v = 0; v < n; ++v)
				{
					// 如果srci->u + u->k 比 srci->k更短 则进行更新
					if (S[v] == false && _matrix[u][v] != MAX_W
						&& dist[u] + _matrix[u][v] < dist[v])
					{
						dist[v] = dist[u] + _matrix[u][v];
						pPath[v] = u;
					}
				}
			}
		}
		void FloydWarshall(vector<vector<W>>& vvDist, vector<vector<int>>& vvpPath)
		{
			size_t n = _vertexs.size();
			vvDist.resize(n);
			vvpPath.resize(n);

			// 初始化权值和路径矩阵
			for (size_t i = 0; i < n; ++i)
			{
				vvDist[i].resize(n, MAX_W);
				vvpPath[i].resize(n, -1);
			}
			//vvpPath[i][j]表示i->j,j的父亲为i




			// 直接相连的边更新一下
			//把目前已知直接相连的边放入vvDist中,并更新vvpPath[i][j]
			for (size_t i = 0; i < n; ++i)
			{
				for (size_t j = 0; j < n; ++j)
				{
					if (_matrix[i][j] != MAX_W)
					{
						vvDist[i][j] = _matrix[i][j];
						vvpPath[i][j] = i;
					}

					if (i == j)
					{
						vvDist[i][j] = W();
					}
				}
			}

			 
			// 最短路径的更新i-> {其他顶点} ->j
			//这里要进行k次的原因是因为我们所有结点都有可能
			//成为src与dst的中间结点,所以要考虑所有情况
			for (size_t k = 0; k < n; ++k)
			{
				for (size_t i = 0; i < n; ++i)
				{
					for (size_t j = 0; j < n; ++j)
					{
						// k 作为的中间点尝试去更新i->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];

							 

							vvpPath[i][j] = vvpPath[k][j];
							//因为这里k实际上是中间顶点集合
							// 找跟j相连的上一个邻接顶点
							// 如果k->j 直接相连,上一个点就k,vvpPath[k][j]存就是k
							// 如果k->j 没有直接相连,k->...->x->j,vvpPath[k][j]存就是x
						}
					}
				}

				// 打印权值和路径矩阵观察数据
				for (size_t i = 0; i < n; ++i)
				{
					for (size_t j = 0; j < n; ++j)
					{
						if (vvDist[i][j] == MAX_W)
						{
							//cout << "*" << " ";
							printf("%3c", '*');
						}
						else
						{
							//cout << vvDist[i][j] << " ";
							printf("%3d", vvDist[i][j]);
						}
					}
					cout << endl;
				}
				cout << endl;

				for (size_t i = 0; i < n; ++i)
				{
					for (size_t j = 0; j < n; ++j)
					{
						//cout << vvParentPath[i][j] << " ";
						printf("%3d", vvpPath[i][j]);
					}
					cout << endl;
				}
				cout << "=================================" << endl;
			}
		}
	private:
		vector<V>_vertexs;//顶点集合
		map<V, int>_indexMap;//存顶点与数组下标的映射关系
		vector<vector<W>>_matrix;//邻接矩阵
	};



 }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/266181.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

FPGA设计时序约束十三、Set_Data_Check

目录 一、序言 二、Set Data Check 2.1 基本概念 2.2 设置界面 2.3 命令语法 三、工程示例 3.1 工程代码 3.2 约束设置 3.3 时序报告 四、参考资料 一、序言 通常进行时序分析时&#xff0c;会考虑触发器上时钟信号与数据信号到达的先后关系&#xff0c;从而进行setu…

文字编辑软件,批量给多个文本添加文档内容

在当今信息爆炸的时代&#xff0c;文字编辑工作是很多人需要面对的&#xff0c;而怎么快速的完成编辑工作&#xff0c;则是很多人所思考解决的。现在有一款很好用的软件——首助编辑高手&#xff0c;可以批量对多个文本文档内容进行处理&#xff0c;能帮你在文字编辑的工作上节…

开关电源厚膜集成电路引脚功能

开关电源厚膜集成电路引脚功能 一、 STR51213、STR50213、STR50103 引脚号 引脚功能 1 接地&#xff0c;内接稳压基准电路 2 开关管基极 3 开关管集电极 4 开关管发射极 5 误差比较电压信号输入&#xff0c;兼待机控制 二、 STR3302、STR3202 引脚号 引脚功能 1内部半…

融资项目——swagger2接口分类配置

在一般开发中&#xff0c;各种Controller可能会被分为两种&#xff1a;后台管理员的相关Controller与用户的相关Controller。所以在使用swagger2的时候&#xff0c;我们也希望其分为两个大类。其解决方法如下&#xff1a; Configuration EnableSwagger2 public class Swagger2…

基于docker-compose 安装Sonar并集成gitlab

文章目录 1. 前置条件2. 编写docker-compose-sonar.yml文件3. 集成 gitlab4. Sonar Login with GitLab 1. 前置条件 安装docker-compose 安装docker 创建容器运行的特有网络 创建挂载目录 2. 编写docker-compose-sonar.yml文件 version: "3" services:sonar-postgre…

DFS与BFS算法总结

知识概览 DFS、BFS都可以对整个问题空间进行搜索&#xff0c;搜索的结构都是像一棵树。DFS会尽可能往深搜&#xff0c;当搜索到叶节点时就会回溯。而BFS每一次只会扩展一层。 DFS与BFS的区别&#xff1a; 搜索方式数据结构空间复杂度性质DFS栈O(h)&#xff0c;其中h为搜索空间…

Epson打印机连接wifi

环境 Epson L3153 打印机联通无线光猫 背景 最近家里的联通宽带不太稳定&#xff0c;经常断网。今天打了联通客服电话&#xff0c;师傅上门来&#xff0c;说可能是光猫用的时间太长了&#xff0c;换了一个新的联通光猫&#xff0c;问题解决。 wifi的名称是 CU_Y3ft 和 CU_Y3…

ARM 点灯

.text .global _start _start: led1设置GPIOE时钟使能 RCC_MP_AHB4ENSETR[4]->1 0X50000A28LDR R0,0X50000A28 指定寄存器地址LDR R1,[R0] 将寄存器数值取出来放在R1中ORR R1,R1,#(0x1<<4) 将第4位设置为1STR R1,[R0] 将修改后的值写回去设置PE10为输出 GPIOE…

RocketMQ事务消息实现分布式事务

文章目录 简介实现原理实现逻辑 简介 RocketMQ事务消息 RocketMQ在4.3.0版中支持分布式事务消息&#xff0c;这里RocketMQ的事务消息是采用2PC(两段式协议) 补偿机制&#xff08;消息回查&#xff09;的分布式事务功能。提供消息发送与业务落库的一致性。 RocketMQ事务消息&am…

强化学习(五)-Deterministic Policy Gradient (DPG) 算法及公式推导

针对连续动作空间&#xff0c;策略函数没法预测出每个动作选择的概率。因此使用确定性策略梯度方法。 0 概览 1 actor输出确定动作2 模型目标&#xff1a; actor目标&#xff1a;使critic值最大 critic目标&#xff1a; 使TD error最大3 改进&#xff1a; 使用两个target 网络…

Redis缓存数据一致性

实际业务中常使用Redis缓存来提升读写效率&#xff0c;减少存储层的压力。因为数据在缓存和DB中各存储一份&#xff0c;所以会出现数据一致性的问题。总体来说导致数据不一致的原因主要有两个。请求并发和操作非原子。 请求并发是指同时可能有多个读写请求同时请求Cache或者DB&…

【C++】bind绑定包装器全解(代码演示,例题演示)

前言 大家好吖&#xff0c;欢迎来到 YY 滴C系列 &#xff0c;热烈欢迎&#xff01; 本章主要内容面向接触过C的老铁 主要内容含&#xff1a; 欢迎订阅 YY滴C专栏&#xff01;更多干货持续更新&#xff01;以下是传送门&#xff01; YY的《C》专栏YY的《C11》专栏YY的《Linux》…

非线性约束的优化问题_序列二次规划算法代码

1. 理论部分 2. 序列二次规划算法代码及解析 3.完整代码 1.理论部分 a.约束优化问题的极值条件 库恩塔克条件(Kuhn-Tucker conditions&#xff0c;KT条件)是确定某点为极值点的必要条件。如果所讨论的规划是凸规划&#xff0c;那么库恩-塔克条件也是充分条件。 &#xff…

5.OpenResty系列之深入理解(一)

本文基于Centos8进行实践&#xff0c;请读者自行安装OpenResty。 1. 内部调用 进入默认安装路径 cd /usr/local/openresty/nginx/conf vim nginx.conflocation /sum {# 只允许内部调用internal;content_by_lua_block {local args ngx.req.get_uri_args()ngx.print(tonumber…

Qt 多线程用法

文章目录 开发平台QThread 类 moveToThreadQtConcurrent::run QFutureWatcherQThreadPool QRunnable 开发平台 项目说明OSwin10 x64Qt6.6compilermsvc2022构建工具cmake QThread 类 moveToThread 写一个简单的例子吧,比较容易理解,方便入门. 也可以看出这种方式,对于线程…

服务器IBM x3650 m2 管理口访问故障处理

服务器的内存告警后&#xff0c;连接管理口查看信息&#xff0c;管理口状态灯显示正常&#xff0c;但是无法ping通和访问。 处理过程如下&#xff1a; 1、在centos 6.6中安装ipmitool&#xff0c;替换为阿里云的yum源&#xff0c;然后安装。 # wget -O /etc/yum.repos.d/Cen…

Unity自带的NavMesh寻路组件

最近看了一下Unity自带的NavMesh寻路组件&#xff0c;先说一下基本的使用&#xff1a; 首先先把AI Navgation的package包给安装上。 给场景地图添加上NavMeshSurface组件&#xff0c;然后进行烘焙&#xff0c;烘焙出对应的场景地图文件。 给移动物体添加对应的Nav MeshAgent组…

【雷达原理】雷达测速原理及实现方法

一、雷达测速原理 1.1 多普勒频率 当目标和雷达之间存在相对运动时&#xff0c;若雷达发射信号的工作频率为&#xff0c;则接收信号的频率为&#xff0c;其中为多普勒频率。将这种由于目标相对于辐射源运动而导致回波信号的频率发生变化的现象称为多普勒效应。 如图1-1所示&a…

FATFS文件系统

文件系统是为了存储和管理数据&#xff0c;而在存储设备上建立的一种组织结构。 Windows常用的文件系统&#xff1a; 1、FAT12 2、FAT16 3、FAT32 4、exFAT 5、NTFS FAT&#xff1a;File Alloction Table 文件分配表 在小型的嵌入式存储设备大多…

Bwapp学习笔记

1.基本sql语句 #求绝对值 select abs(-1) from dual; #取余数 select mod(10,3); #验证show databases结果是取之于schemata表的 show databases; select schema_name from information_schema.schemata; #查询当前的数据库 select database(); -- 查询数据库版本 s…