Hello大家好!很高兴我们又见面啦!给生活添点passion,开始今天的编程之路!

我的博客:<但凡.

我的专栏:《编程之路》、《数据结构与算法之美》、《题海拾贝》、《C++修炼之路》

欢迎点赞,关注!

 

目录

 

1、什么是图

      1.1、图的基本概念

      1.2、图的类型

      1.3、图的表示方法

      1.4、图的应用场景

      1.5、图的常见算法

      1.6、图的相关概念

2、建图

      2.1、邻接矩阵

      2.2、邻接表

3、遍历

      3.1、BFS

      3.2、DFS

 4、最小生成树

      4.1、Kruskal

      4.2、Prim

5、单源最短路算法 

      5.1、Dijkstra

      5.2、Bellman-Ford

      5.3、SPFA

6、多源最短路


 

1、什么是图

      1.1、图的基本概念

        图(Graph)是一种数据结构,由节点(Vertex)和边(Edge)组成,用于表示实体之间的关系。节点代表实体,边表示实体之间的连接或交互。图的数学定义为:

[ G = (V, E) ]


其中,( V ) 是节点集合,( E ) 是边集合。

    1.2、图的类型

  1. 无向图
    边没有方向性,表示双向关系。例如社交网络中的好友关系。
  2. 有向图
    边有方向性,表示单向关系。如网页之间的超链接。
  3. 加权图
    边带有权重,表示关系的强度或成本。如地图中的路径距离。
  4. 无权图
    边无权重,仅表示连接关系。

      1.3、图的表示方法

  1. 邻接矩阵
    用二维数组表示节点间的连接关系。矩阵元素 ( A[i][j] ) 表示节点 ( i ) 和 ( j ) 是否相连。

    • 无向图的邻接矩阵对称。
    • 适合稠密图(边较多)。
  2. 邻接表
    每个节点维护一个链表,存储与其直接相连的节点。

    节省空间,适合稀疏图(边较少)。

      1.4、图的应用场景

  1. 社交网络分析
    节点代表用户,边代表好友关系。
  2. 路径规划
    如导航系统中的最短路径计算(Dijkstra算法)。
  3. 推荐系统
    通过用户-商品二分图挖掘关联规则。
  4. 知识图谱
    用有向图表示实体间的语义关系。

      1.5、图的常见算法

  • 遍历算法
    • 深度优先搜索(DFS)
    • 广度优先搜索(BFS)
  • 最短路径算法
    • Dijkstra(非负权重)
    • Bellman-Ford(含负权重)
    • SPFA(升级版Bellman-Ford)
  • 最小生成树算法
    • Kruskal
    • Prim

        图的理论与实践结合紧密,是计算机科学中解决复杂问题的核心工具之一。

      1.6、图的相关概念

        度(Degree):无向图中与顶点相连的边数。有向图中分为入度(In-Degree)和出度(Out-Degree)。
        路径(Path):顶点序列,通过边依次连接。简单路径不重复经过顶点。
        环(Cycle):起点和终点相同的路径。有向图中称为有向环。
        连通性(Connectivity):无向图中任意两顶点间存在路径则为连通图。有向图中强连通指双向可达。
        子图(Subgraph):顶点和边均为原图子集的图。

2、建图

      2.1、邻接矩阵

        主要成员变量包含_vIndexMap,_vertexs,_matrix。其中,_vIndexMap是存储每个顶点在邻接矩阵中的映射值,_vertexs存储所有的顶点,_matrix是存储图的矩阵。

        图的类默认是无向图,如果两点之间没有路,我们就把这两点在邻接矩阵中的值设置为INT_MAX。

        我们使用成员函数GetVertexIndex获得某个顶点的映射值,使用成员函数AddEdge和_AddEdge来在邻接矩阵中添加边。

namespace Matrix
{//        顶点    权重            template<class V,class W,W MAX_W=INT_MAX,bool Direction=false>class Graph{public:struct Edge//边{V _srci;V _dsti;W _w;Edge(const V& srci,const V& dsti,const W& w):_srci(srci),_dsti(dsti),_w(w){}bool operator<(const Edge& eg) const{return _w < eg._w;}bool operator>(const Edge& eg) const{return _w > eg._w;}};typedef Graph<V, W, MAX_W, Direction> Self;Graph() = default;//生成一个默认构造Graph(const V* vertexs, size_t n){_vertexs.reserve(n);for (size_t i = 0;i < n;i++){_vertexs.push_back(vertexs[i]);_vIndexMap[vertexs[i]] = i;	}//MAX_W作为不存在边的标识值_matrix.resize(n);for (auto& e : _matrix){e.resize(n, MAX_W);}}size_t GetVertexIndex(const V& v){auto ret = _vIndexMap.find(v);if (ret != _vIndexMap.end()){return ret->second;}else{throw invalid_argument("不存在的顶点");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& det, const W& w){size_t srci = GetVertexIndex(src);size_t dsti = GetVertexIndex(det);_AddEdge(srci, dsti, w);}private:map<V, size_t> _vIndexMap;vector<V> _vertexs;//顶点集合vector<vector<W>> _matrix;//存储边集合的矩阵};
}

      2.2、邻接表

         邻接表也有三个成员变量,分别是_vertexs,_indexMap,_tables。_vertexs是顶点集合,_indexMap记录顶点的映射下标,_tables是邻接表。

namespace link_table
{template<class W>struct Edge{int _dsti;W _w;Edge<W>* _next;Edge(int dsti,const W& w):_dsti(dsti),_w(w),_next(nullptr){}};template<class V,class W,bool Direction=false>class Graph{typedef Edge<W> Edge;public:Graph(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;}_tables.resize(n, nullptr);}size_t GetVertexIndex(const V& v){auto it = _indexMap.find(v);if (it != _indexMap.end()){return it->second;}else{throw invalid_argument("顶点不存在");return -1;}}void AddEdge(const V& src, const V& dst, const W& w){size_t srci = GetVertexIndex(src);size_t dsti = GetVertexIndex(dst);Edge* eg = new Edge(dsti, w);eg->_next = _tables[srci];_tables[srci] = eg;if (Direction == false){Edge* eg = new Edge(srci, w);eg->_next = _tables[dsti];_tables[dsti] = eg;}}void Print(){for (size_t i = 0;i < _vertexs.size();i++){cout << "[" << i << "]" << "->" << _vertexs[i] << endl;}cout << endl;for (size_t i = 0;i < _tables.size();i++){cout << _vertexs[i] << "[" << i << "]->";Edge* cur = _tables[i];while (cur){cout << "[" << _vertexs[cur->_dsti] << ":" << cur->_dsti << ":" << cur->_w << "]->";cur = cur->_next;}cout << "nullptr" << endl;}}private:vector<V> _vertexs;//顶点集合map<V, int> _indexMap;//顶点映射下标vector<Edge*> _tables;//邻接表};
}

         接下来我们各种有关图的算法都使用邻接矩阵的图实现。

3、遍历

      3.1、BFS

        我们给定遍历开始的起点,然后按照BFS算法,依次遍历每一层节点。

        我们根据图来理解一下,首先左上角的节点是起点,第一次,我们是把和起点相连的节点加入到队列中,接着我们设置好dsize,第二次遍历的时候就会把第一层的两个节点取出,然后我们再把和第一层相连的几个节点加入到队列中,第三层就是把和第二层相连的节点加入到队列中。

void BFS(const V& src)
{size_t srcindex = GetVertexIndex(src);vector<bool> visited;visited.resize(_vertexs.size(), false);queue<int> q;q.push(srcindex);visited[srcindex] = true;size_t d = 1;size_t dSize = 1;while (!q.empty()){printf("%s的%d度好友:",src.c_str(), d);//其实就是从src开始第几层好友while (dSize--){size_t front = q.front();q.pop();for (size_t i = 0;i < _vertexs.size();i++){if (visited[i] == false && _matrix[front][i] != MAX_W){printf("[%d:%s]", i, _vertexs[i].c_str());visited[i] = true;q.push(i);}}}cout << endl;dSize = q.size();++d;}cout << endl;
}

      3.2、DFS

        注意我这里写的dfs只使用于存储值为字符串的,其他版本的大家自己改一下就好了。

        

        对于bfs来说,我们可以理解为能走就走,走不了就回退。 

void _DFS(size_t srcIndex, vector<bool>& visited)
{printf("[%d:%s]->", srcIndex, _vertexs[srcIndex].c_str());visited[srcIndex] = true;for (size_t i = 0;i < _vertexs.size();i++){if (visited[i] == false && _matrix[srcIndex][i] != MAX_W){_DFS(i, visited);}}
}void DFS(const V& src)
{size_t srcindex = GetVertexIndex(src);vector<bool> visited;visited.resize(_vertexs.size(), false);_DFS(srcindex, visited);cout << endl;
}

 4、最小生成树

        最小生成树(Minimum Spanning Tree,MST)是指在一个带权无向连通图(注意一定是连通图)中,选取一棵包含所有顶点的树,且树的边权值之和最小。它满足以下条件:

  • 包含图中所有顶点。
  • 是原图的子图且无环(即为一棵树)。
  • 所有边的权值总和最小。

       常见的最小生成树算法

        Kruskal算法
        基于贪心策略,按边权从小到大排序,依次选择不形成环的边,直到覆盖所有顶点。适合稀疏图。

        Prim算法
        从一个顶点出发,逐步选择与当前树相连的最小权边,扩展树直至覆盖所有顶点。适合稠密图。

        接下来我们实现以下这两种算法:

      4.1、Kruskal

        我在这里实现的kruskal算法运用了并查集,大家可以看我上一期文章来实现并查集。

        kruskal我们可以理解为全局贪心,我们直接把所有边都加入到堆中,接着,依次取出堆中的每个边,如果这个边中的起点和终点不存在一个并查集中,说明这个边是合法的,我们把这个边加入到最小生成树中。直到取出n-1条边,其中n是节点的个数。

        我们结合图来理解一下:

        kruskal算法的时间复杂度为O(n logn),另外,我这里的kruskal算法是使用堆实现的,如果使用库中自带的sort效率会略低一些,因为我们要考虑到,有一部分边我们不需要排序他们,也就是说大部分情况下,我们不需要拿出所有的边才能生成这个最小生成树,对于权重较大的边我们不需要排序。

        kruskal算法适用于稀疏图,在稠密图中,排序阶段可能成为性能瓶颈。若边数接近顶点平方,采用Prim算法(基于优先队列)可能更高效。

W Kruskal(Self& minTree)
{//全局贪心minTree._vertexs = _vertexs;minTree._vIndexMap = _vIndexMap;minTree._matrix.resize(_vertexs.size());for (auto& e : minTree._matrix){e.resize(_vertexs.size(), MAX_W);}//所有的边都先入堆priority_queue<Edge, vector<Edge>, greater<Edge>> pq;for (size_t i = 0;i < _matrix.size();i++){for (size_t j = 0;j < _matrix.size();j++){if (i < j && _matrix[i][j] != MAX_W){pq.push(Edge(i, j, _matrix[i][j]));}}}W total = W();size_t i = 1;UnionFindSet ufs(_vertexs.size());while (i < _vertexs.size() && !pq.empty()){Edge min = pq.top();pq.pop();if (ufs.FindRoot(min._srci) != ufs.FindRoot(min._dsti)){minTree._AddEdge(min._srci, min._dsti, min._w);total += min._w;ufs.Union(min._srci, min._dsti);++i;}}if (i == _vertexs.size()){return total;}else{return W();//说明这个图不连通}
}

      4.2、Prim

        Prim算法是一种用于求解加权无向图最小生成树(Minimum Spanning Tree, MST)的贪心算法。该算法从任意顶点开始,逐步扩展生成树,每次选择连接生成树与非生成树顶点且权值最小的边,直至所有顶点都被包含在生成树中。Prim算法适合稠密图,时间复杂度为O()。

        Prim算法我们可以理解为局部贪心。从起点开始,每次拿边都是拿以当前这个点为起点的边中权重最小的那个。同样,Prim算法的结束条件也是抽到n-1条边。在下面的代码实现中,我用X,Y两个数组作为标记数组,判断选取某个边是否会形成环。

        对于最小生成树的两个算法来说,都是对边进行选择,而和顶点的标号没有关系。 

W Prim(Self& minTree, const V& src)
{size_t srci = GetVertexIndex(src);size_t n = _vertexs.size();minTree._vertexs = _vertexs;minTree._vIndexMap = _vIndexMap;minTree._matrix.resize(n);for (size_t i = 0;i < _vertexs.size();i++){minTree._matrix[i].resize(n, MAX_W);} vector<bool> X(n, false);vector<bool> Y(n, true);X[srci] = true;Y[srci] = false;priority_queue<Edge, vector<Edge>, greater<Edge>> minq;//先把srci链接的边添加到队列中for (int i = 0;i < n;i++){if (_matrix[srci][i] != MAX_W){minq.push(Edge(srci, i, _matrix[srci][i]));}}cout << "开始选边" << endl;size_t size = 0;W totalW = W();while (!minq.empty()){Edge min = minq.top();minq.pop();if (X[min._dsti]){//这个边的末尾的节点已经作为开始节点选过了,所以成环,所以这个边不能要}else{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();}
}

5、单源最短路算法 

        单源最短路问题是指在加权图(有向或无向)中,从给定源点到其他所有顶点的最短路径问题。

         单源最短路算法包含三个,Dijkstra算法,Bellman-Ford算法,spfa算法。

      5.1、Dijkstra

        Dijkstra算法适用于边权为非负的图。算法通过贪心策略逐步扩展最短路径树。注意,Dijkstra算法不实用于存在负权边的图。以下实现的Dijkstra时间复杂度为O()。

        对于Dijkstra算法来说,每次我们确定一个顶点的最短路径,所以说最外层循环进行n轮。对于每轮循环,我们查找dist数组中的最小值,以这个最小值对应的节点为起点开始对周边节点进行更新。我们结合下图理解一下:

void Dijkstra(const V& src, vector<W>& dist, vector<int>& pPath)
{size_t srci = GetVertexIndex(src);size_t n = _vertexs.size();dist.resize(n, MAX_W);pPath.resize(n, -1);dist[srci] = 0;pPath[srci] = srci;vector<bool> S(n, false);for (size_t j = 0;j < n;j++){//选择最短路径点且不再S更新其他路径int u = 0;W min = MAX_W;for (size_t i = 0;i < n;i++){if (S[i] == false && dist[i] < min){u = i;min = dist[i];}}S[u] = true;for (size_t v = 0;v < n;v++){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;}}}
}

      5.2、Bellman-Ford

        Bellman-Ford算法是一种用于求解单源最短路径问题的算法,能够处理带有负权边的图。与Dijkstra算法不同,Bellman-Ford适用于存在负权边但无负权环的情况,并能检测图中是否存在负权环。以下算法的实现时间复杂度为O()。

        通过松弛操作(Relaxation)逐步逼近最短路径。对于包含(V)个顶点和(E)条边的图,算法进行(V-1)轮松弛操作,每轮遍历所有边。若在第(V)轮仍能松弛,则说明存在负权环。所谓松弛操作,其实就是更新某一个节点的dist。我们最多经过n轮松弛操作,因为每轮松弛操作至少确定一个顶点的最短路径。如果n轮松弛操作后还能继续松弛,就说明这个图中存在负环。

        下图情况是只需要更新一轮的情况。第一轮更新完毕,第二轮没有更新,在第二轮循环结束后跳出大循环。

bool BellmanFord(const V& src, vector<W>& dist, vector<int>& pPath)
{size_t n = _vertexs.size();size_t srci = GetVertexIndex(src);dist.resize(n, MAX_W);pPath.resize(n, -1);dist[srci] = W();//最多进行n轮for (size_t k = 0;k < n;k++){bool update = false;cout << "更新第:" << k << "轮" << endl;for (size_t i = 0;i < n;i++){for (size_t j = 0;j < n;j++){if (_matrix[i][j] != MAX_W && dist[i] != MAX_W && dist[i] + _matrix[i][j] < dist[j]){update = true;dist[j] = dist[i] + _matrix[i][j];pPath[j] = i;}}}//倘若有一轮没有松弛,说明已经更新完了,直接跳出循环。if (update == false){break;}}//如果还能更新就说明有负环for (size_t i = 0;i < n;i++){for (size_t j = 0;j < n;j++){if (_matrix[i][j] != MAX_W && dist[i]!=MAX_W&&dist[i] + _matrix[i][j] < dist[j]){return false;}}}return true;
}

      5.3、SPFA

        SPFA(Shortest Path Faster Algorithm)是一种用于求解单源最短路径的算法,适用于带有负权边的图。它是Bellman-Ford算法的优化版本,通过动态调整队列结构减少冗余计算,从而提高效率。该算法平均时间复杂度为O(n)。

        算法利用队列优化松弛操作。每次从队列中取出一个节点,对其邻接节点进行松弛操作。若邻接节点的距离被更新且不在队列中,则将其加入队列。重复这一过程直到队列为空。

         其实SPFA算法的本质就是在bellman-ford的基础上,发现对于每轮更新来说,如果某一个节点上一轮没有变化,说明这个节点已经确定了,我们在当前轮就没必要更新他周围的边了。所以说对于松弛操作,我们就只需要把当前轮被改变的那个节点加入队列就好。倘若存在一个节点加入队列,移除队列这个操作循环了n次及以上,说明这个图有负环。

bool SPFA(const V& src, vector<W>& dist, vector<int>& pPath) {size_t n = _vertexs.size();size_t srci = GetVertexIndex(src);dist.resize(n, MAX_W);pPath.resize(n, -1);dist[srci] = W();queue<size_t> q;q.push(srci);vector<bool> inQueue(n, false);inQueue[srci] = true;vector<size_t> count(n, 0); // 记录节点入队次数,用于检测负环while (!q.empty()) {size_t u = q.front();q.pop();inQueue[u] = false;for (size_t v = 0; v < n; ++v) {if (_matrix[u][v] != MAX_W && dist[u] != MAX_W && dist[u] + _matrix[u][v] < dist[v]) {dist[v] = dist[u] + _matrix[u][v];pPath[v] = u;if (!inQueue[v]) {q.push(v);inQueue[v] = true;count[v]++;if (count[v] >= n) {return false; // 存在负环}}}}}return true;
}

6、多源最短路

        这里就介绍一个Floyd算法。Floyd-Warshall算法是一种用于寻找加权图中所有顶点对之间最短路径的动态规划算法。该算法适用于有向图或无向图,可以处理负权边(但不允许存在负权回路)。其时间复杂度为O(N^3),其中N是图中的顶点数。

        我们看最主要的三轮循环,最外一层循环的k我们叫他中转点,也就是说,我们把每个点是中转点的情况都考虑一下,然后再遍历以这个点为中转点的所有情况,如果,从i到k的距离加从k到j的距离小于从i到j的距离,那么就更新dist。

        这个就不画图了,太难画了。

void FloydWarshall(vector<vector<W>>& vvDist, vector<vector<int>>& vvpPath)
{size_t n = _vertexs.size();vvDist.resize(n);vvpPath.resize(n);//vvpPath设置的是到达j的前驱节点for (size_t i = 0;i < n;i++){vvDist[i].resize(n, MAX_W);vvpPath[i].resize(n, -1);}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();}}}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];vvpPath[i][j] = vvpPath[k][j];                                              }}}}for (size_t i = 0;i < n;i++){for (size_t j = 0;j < n;j++){if (vvDist[i][j] == MAX_W){printf("%3c", '*');}else{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++){printf("%3d", vvpPath[i][j]);}cout << endl;}cout << "********************************" << endl;
}

        以上就是我们需要掌握的和图有关的内容。需要注意的是,在以上的各种算法中我大多都是实现的朴素版本,或者说是传统版本这些算法在其他的数据结构的优化下可能会有一些小提升,但是大体思路是不变的。 另外,在算法比赛中,无论是建图,还是各种算法,我们的代码量都会打一个折扣,因为以上代码是基于动态的图结构实现的。但是无论如何我们建图,各种算法的思想是不变的。

        好了,今天的内容就分享到这,我们下期再见!

 

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SpringBoot -- 热部署

9.SpringBoot 热部署&#xff08;自动重启&#xff09; 在实际开发过程中&#xff0c;每次修改代码就得将项目重启&#xff0c;重新部署&#xff0c;对于一些大型应用来说&#xff0c;重启时间需要花费大量的时间成本。对于一个后端开发者来说&#xff0c;重启过程确实很难受啊…

HarmonyOS 5浏览器引擎对WebGL 2.0的支持如何?

以下是HarmonyOS 5浏览器引擎对‌WebGL 2.0‌支持的详细技术分析&#xff1a; 一、核心支持能力 ‌系统能力声明 HarmonyOS 5 浏览器引擎通过 SystemCapability.Graphic.Graphic2D.WebGL2 提供对 WebGL 2.0 的底层支持 支持的关键特性包括&#xff1a; OpenGL ES 3.0 特性…

Class1线性回归

Class1线性回归 买房预测 要根据历史数据来预测一套房子的价格。你发现影响房价的因素有很多&#xff0c;于是你决定使用线性回归模型来预测房价。 影响房价的因素如下&#xff1a; 房屋面积&#xff08;平方米&#xff09; 房龄&#xff08;年&#xff09; 离地铁站的距离&a…

Vue.js 3:重新定义前端开发的进化之路

Vue.js 3&#xff1a;重新定义前端开发的进化之路 引言&#xff1a;一场酝酿已久的革新 2020年9月18日&#xff0c;Vue.js团队以代号"One Piece"正式发布3.0版本&#xff0c;这不仅是框架发展史上的重要里程碑&#xff0c;更是前端工程化领域的一次革命性突破。历经…

Unity性能优化-渲染模块(1)-CPU侧(1)-优化方向

Unity 中渲染方面的优化大致可以划分为以下几块核心内容&#xff1a; CPU 优化 (减少 Draw Calls 和 CPU 瓶颈) GPU 优化 (减少像素着色和 GPU 瓶颈) 内存和显存优化 (Resource Management) 光照优化 (Lighting & Global Illumination) 这四个方面是相互关联的。一个方…

AI矢量图与视频无痕修复:用Illustrator与After Effects解锁创作新维度

最近因一个项目&#xff0c;有机会深度体验了奥地利Blueskyy艺术学院授权的Adobe教育版全家桶&#xff0c;过程中发现了不少令人惊喜的“黑科技”&#xff0c;很想和大家分享这份发掘宝藏的喜悦。一句话总结这次体验&#xff1a;慷慨且稳定。比如&#xff0c;它每周提供高达150…

Maven Javadoc 插件使用详解

Maven Javadoc 插件使用详解 maven-javadoc-plugin 是 Maven 项目中用于生成 Java API 文档的标准插件&#xff0c;它封装了 JDK 的 javadoc 工具&#xff0c;提供了更便捷的配置和集成方式。 一、基本使用 1. 快速生成 Javadoc 在项目根目录执行以下命令&#xff1a; bas…

Apache Kafka 面试应答指南

Apache Kafka 核心知识详解与面试应答指南 一、Apache Kafka 概述 Apache Kafka 作为一款分布式流处理框架,在实时构建流处理应用领域发挥着关键作用。其最广为人知的核心功能,便是作为企业级消息引擎被众多企业采用。 二、消费者组 (一)定义与原理 消费者组是 Kafka 独…

在NVIDIA Jetson和RTX上运行Google DeepMind的Gemma 3N:多模态AI的边缘计算革命

在NVIDIA Jetson和RTX上运行Google DeepMind的Gemma 3N&#xff1a;多模态AI的边缘计算革命 文章目录 在NVIDIA Jetson和RTX上运行Google DeepMind的Gemma 3N&#xff1a;多模态AI的边缘计算革命引言&#xff1a;多模态AI进入边缘计算时代文章结构概览 第一章&#xff1a;Gemma…

iOS打包流程中的安全处理实践:集成IPA混淆保护的自动化方案

随着iOS应用上线节奏的加快&#xff0c;如何在持续集成&#xff08;CI&#xff09;或交付流程中嵌入安全处理手段&#xff0c;成为开发团队构建自动化发布链路时不可忽视的一环。特别是在App已经完成构建打包&#xff0c;准备分发前这一阶段&#xff0c;对IPA进行结构层面的加固…

FFmpeg进行简单的视频编辑与代码写法实例

使用 FFmpeg 进行简单的视频编辑非常强大。它是一个命令行工具&#xff0c;虽然一开始可能看起来有点复杂&#xff0c;但掌握了基本命令后会非常有用。 以下是一些常见的简单视频编辑操作及其 FFmpeg 命令&#xff1a; 1. 剪切视频 如果你想从一个视频中剪切出一段&#xff0…

如何使用免费软件写论文?六个免费论文生成软件使用指南

在学术写作中&#xff0c;利用AI技术和免费的写作工具可以极大地提高效率&#xff0c;尤其对于需要处理大量文献、结构化写作的论文来说&#xff0c;使用合适的软件能节省时间&#xff0c;提升论文质量。这里为您推荐六个免费的论文生成软件&#xff0c;并提供使用指南&#xf…

大数据系统架构实践(二):Hadoop集群部署

大数据系统架构实践&#xff08;二&#xff09;&#xff1a;Hadoop集群部署 文章目录 大数据系统架构实践&#xff08;二&#xff09;&#xff1a;Hadoop集群部署一、Hadoop简介二、部署前准备三、部署Hadoop集群1. 下载并解压安装包2. 配置hadoop-env.sh3. 配置core-site.xml4…

42道Maven高频题整理(附答案背诵版)

1.简述什么是Maven&#xff1f; Maven是一个项目管理和构建自动化工具&#xff0c;主要服务于Java项目。使用Maven&#xff0c;开发者可以方便地管理项目的构建、文档生成、报告、依赖、SCM&#xff08;软件配置管理&#xff09;、发布和分发等过程。 Maven的核心概念是基于项…

【数字后端】- 如何进行时钟树综合?

首先&#xff0c;要明确的是&#xff0c;时钟树综合只有命令去操作这一种方式 CTS的步骤 1、时钟树综合前的准备工作-设置时钟树cell&#xff08;每个项目必做&#xff09; 最简单的项目要设置生长时钟树时可用的clock buffer和clock inverter cell list&#xff0c;如下 此…

OpenCV CUDA模块设备层-----像素值进行逐通道的最大值比较函数max()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 是 OpenCV 的 CUDA 模块&#xff08;cv::cudev&#xff09; 中的一个 设备端内联函数&#xff08;device function&#xff09;&#xff0c;用于…

SCSAI万物对象模型和五维市场交易平台原型

一个完整的工业软件对象模型平台&#xff0c;基于SCSAI对象模型和五维市场理论。该平台包含对象管理、五维市场交易和大模型集成功能。 工业软件对象模型平台功能说明 这个平台实现了基于Aras Innovator对象模型和五维市场理论的工业软件解决方案&#xff0c;主要功能包括&…

昇腾のPrefix Cache

Prefix Cache特性介绍 Prefix Cache 即前缀缓存&#xff0c;是一种用于优化大语言模型&#xff08;LLM&#xff09;推理性能的技术&#xff0c;主要应用于多轮对话、系统提示等具有大量共同前缀的场景。 原理 LLM 推理计算主要包括 Prefill 阶段&#xff08;Prompt 计算&…

12-C#的list数据使用

C#的list数据使用 1.实例化 List<double> lst1 new List<double>();2.数据清除 lst1 .Clear();3.数据清除

SQL数据迁移利器:INSERT INTO SELECT语句详解

引言 在数据库操作中&#xff0c;我们经常需要将一个表中的数据迁移或复制到另一个表中。这时候&#xff0c;INSERT INTO SELECT语句就成为了一个极其有用的工具。今天我们就来深入探讨这个强大的SQL语句。 基本语法 INSERT INTO 目标表(字段1, 字段2, ...) SELECT 字段1, 字…