用php做电商网站html视频网站源码
- 作者: 五速梦信息网
- 时间: 2026年04月20日 06:56
当前位置: 首页 > news >正文
用php做电商网站,html视频网站源码,wix和wordpress,wordpress主题08影视文章目录 一、图的基本概念二、图的储存结构1、邻接矩阵2、邻接表 三、图的遍历1、广度优先遍历2、深度优先遍历 四、最小生成树1、概念2、Kruskal算法3、Prim算法 五、最短路径问题1、单源最短路径–Dijkstra算法2、单源最短路径–Bellman-Ford算法3、多源最短路径–Floyd-War… 文章目录 一、图的基本概念二、图的储存结构1、邻接矩阵2、邻接表 三、图的遍历1、广度优先遍历2、深度优先遍历 四、最小生成树1、概念2、Kruskal算法3、Prim算法 五、最短路径问题1、单源最短路径–Dijkstra算法2、单源最短路径–Bellman-Ford算法3、多源最短路径–Floyd-Warshall算法 六、拓扑排序1、概念2、如何排序3、实现4、应用 一、图的基本概念 图是由顶点集合及顶点间的关系组成的一种数据结构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条边记作ekek (vivj)或vivj。有向图和无向图在有向图中顶点对x, y是有序的顶点对xy称为顶点x到顶点y的一条边(弧)x, y和y, x是两条不同的边比如。在无向图中顶点对(x, y)是无序的顶点对(x,y)称为顶点x和顶点y相关联的一条边这条边没有特定方向(x, y)和(yx)是同一条边。注意无向边(x, y)等于有向x, y和y, x。完全图在有n个顶点的无向图中若有n * (n-1)/2条边即任意两个顶点之间有且仅有一条边则称此图为无向完全图在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的路径。路径长度对于不带权的图一条路径的路径长度是指该路径上的边的条数对于带权的图一条路径的路径长度是指该路径上各个边权值的总和。简单路径与回路若路径上各顶点v1v2v3…vm均不重复则称这样的路径为简单路径。若路径上第一个顶点v1和最后一个顶点vm重合则称这样的路径为回路或环。子图设图G {V, E}和图G1 {V1E1}若V1属于V且E1属于E则称G1是G的子图。连通图在无向图中若从顶点v1到顶点v2有路径则称顶点v1与顶点v2是连通的。如果图中任意一对顶点都是连通的则称此图为连通图。强连通图在有向图中若在每一对顶点vi和vj之间都存在一条从vi到vj的路径也存在一条从vj到vi的路径则称此图是强连通图。生成树一个连通图的最小连通子图称作该图的生成树。有n个顶点的连通图的生成树有n个顶点和n-1条边 二、图的储存结构 因为图中既有节点又有边(节点与节点之间的关系)因此在图的存储中只需要保存节点和边关系即可。 1、邻接矩阵 因为节点与节点之间的关系就是连通与否即为0或者1因此邻接矩阵(二维数组)即是先用一个数组将定点保存然后采用矩阵来表示节点与节点之间的关系。 注意 1如果有权值的话相连的边数组保存边的权值不相连的边一般用无穷大表示。 2无向图的邻接矩阵是对称的有向图则不是。 3邻接矩阵的优点是可以快速查找两个顶点是否相连缺点是如果顶点多而边少就会造成空间浪费。 代码实现 // V - 顶点类型W - 权值类型, MAX_W - 权值最大值 // Direction - 是否有向 true - 无向 false - 无向 templateclass V, class W, W MAX_W INT_MAX, bool Direction false class Graph {typedef GraphV, W, MAX_W, Direction Self;public:Graph() default;Graph(const V* vertexs, size_t n){//初始化_vertexs.resize(n);_matrix.resize(n);for (int i 0; i n; i){//初始化矩阵_matrix[i].resize(n, MAX_W);//初始化映射关系_vertexs[i] vertexs[i];_vIndexMap[vertexs[i]] i;}}//获取映射下标size_t GetVertexIndex(const V v){//查找是否存在auto ret _vIndexMap.find(v);//存在返回索引否则返回-1if (ret ! _vIndexMap.end())return ret-second;else cout不存在顶点endl;return -1;}void _AddEdge(size_t srci, size_t dsti, const W w){//默认有向_matrix[srci][dsti] w;//无向if (Direction){_matrix[dsti][srci] w;}}//添加边void AddEdge(const V src, const V dst, const W w){//获取下标int srci GetVertexIndex(src);int dsti GetVertexIndex(dst);//判断是否存在if (srci 0 dsti 0)_AddEdge(srci, dsti, w);}private:mapV, size_t _vIndexMap;//元素与下标的映射vectorV _vertexs; // 顶点集合vectorvectorW _matrix; //矩阵 }下面其他算法均使用邻接矩阵实现。 2、邻接表 邻接表使用数组表示顶点的集合使用链表表示边的关系。 无向图邻接表存储 注意无向图中同一条边在邻接表中出现了两次。如果想知道顶点vi的度只需要知道顶点vi边链表集合中结点的数目即可。 有向图邻接表存储 注意有向图中每条边在邻接表中只出现一次与顶点vi对应的邻接表所含结点的个数就是该顶点的出度也称出度表要得到vi顶点的入度必须检测其他所有顶点对应的边链表看有多少边顶点的dst取值是i。 实现 只实现出边表一般只会用到。 //顶点集合//W - 权值类型templateclass Wstruct LinkEdge{int _srcIndex;int _dstIndex;W _w;LinkEdgeW* _next;LinkEdge(int srcIndex , int dstIndex , const W w): _srcIndex(srcIndex), _dstIndex(dstIndex), _w(w), _next(nullptr){}};//V - 顶点类型W - 权值类型// Direction - 是否有向 true - 无向 false - 无向templateclass V, class W, bool Direction falseclass Graph{typedef LinkEdgeW Edge;public:Graph() default;//初始化Graph(const V* vertexs, size_t n){//初始化_linkTable.resize(n,nullptr);_vertexs.resize(n);for (int i 0; i n; i){//初始化映射关系_vertexs[i] vertexs[i];_vIndexMap[vertexs[i]] i;}}//获取下表size_t GetVertexIndex(const V v){//查找是否存在auto ret _vIndexMap.find(v);//存在返回索引否则返回-1if (ret ! _vIndexMap.end())return ret-second;elsecout 不存在该顶点 endl;return -1;}//添加边void AddEdge(const V src, const V dst, const W w){//获取映射下标int srci GetVertexIndex(src);int dsti GetVertexIndex(dst);if (srci -1 || dsti -1)return;//使用头插法//有向图Edge* srceg new Edge(srci, dsti, w);srceg-_next _linkTable[srci];_linkTable[srci] srceg;//无向图if (Direction){Edge* dsteg new Edge(dsti, srci, w);dsteg-_next _linkTable[dsti];_linkTable[dsti] dsteg;}}private:mapstring, int _vIndexMap; //顶点与下标的映射关系vectorV _vertexs; // 顶点集合vectorEdge* _linkTable; //邻接表};三、图的遍历 1、广度优先遍历 通过一个顶点一层层不断往外扩。 怎么实现 1通过一个数组记录顶点是否被遍历到。 2通过队列每次将队列的顶点拿出并将其指向的顶点入队循环上述操作。 //广度优先遍历 void BFS(const V src) {//获取索引int srci GetVertexIndex(src);//节点数int n _vertexs.size();//标记数组vectorbool vis(n);//通过队列遍历访问queueint q;q.push(srci);vis[srci] true;while (!q.empty()){//一层一层遍历int sz q.size();for (int i 0; i sz; i){//取队头元素int index q.front();q.pop();cout index : _vertexs[index] | ;//将与队头元素连接且灭访问过的点进队并做标记for (int j 0; j n; j){if (vis[j] false _matrix[index][j] ! MAX_W){vis[j] true;q.push(j);}}}cout endl;} }2、深度优先遍历 怎么实现 从一个顶点出发找到下一个顶点再次以这个顶点出发重复上述操作直到找不到下一个顶点就进行回溯。 void _DFS(int index, vectorbool vis) {cout index : _vertexs[index] endl;//将index指向且没有访问过的节点进行标配并递归搜索for (int i 0; i _vertexs.size(); i){if (vis[i] false _matrix[index][i] ! MAX_W){vis[i] true;_DFS(i, vis);}} }//深度优先搜索 void DFS(const V v) { //标记数组vectorbool vis(_vertexs.size());//获取v的索引int srci GetVertexIndex(v);//进行遍历_DFS(srci, vis);//将剩下没有遍历到的节点遍历for (int i 0; i _vertexs.size(); i){if (vis[i] false)_DFS(i, vis);} }四、最小生成树 1、概念 连通图中的每一棵生成树都是原图的一个极大无环子图即从其中删去任何一条边生成树就不在连通反之在其中引入任何一条新边都会形成一条回路。 若连通图由n个顶点组成则其生成树必含n个顶点和n-1条边。因此构造最小生成树的准则有三条 只能使用图中的边来构造最小生成树只能使用恰好n-1条边来连接图中的n个顶点选用的n-1条边不能构成回路 构造最小生成树的方法Kruskal算法和Prim算法。这两个算法都采用了逐步求解的贪心策略。 2、Kruskal算法 任给一个有n个顶点的连通网络N{V,E}首先构造一个由这n个顶点组成、不含任何边的图G{V,NULL}其中每个顶点自成一个连通分量 其次不断从E中取出权值最小的一条边(若有多条任取其一)若该边的两个顶点来自不同的连通分量则将此边加入到G中。如此重复直到所有顶点在同一个连通分量上为止。 核心每次迭代时选出一条具有最小权值且两端点不在同一连通分量上的边加入生成树。 上图来自算法导论。 怎么实现 1使用一个结构体保存边的信息。 2使用优先队列小的在队头将所有边进队。 3开始挑边在挑的过程中使用一个并查集将选顶点和不选的顶点分成两个集合判断是否成环直到挑到n-1n顶点的个数条边结束。 //并查集 #includeiostream #includevectorusing namespace std;class UnionFindSet { public:UnionFindSet(size_t size):_ufs(size, -1){}// 给一个元素的编号找到该元素所在集合的名称int FindRoot(int index){int root index;while (_ufs[root] 0){root _ufs[root];}while(_ufs[index] 0){int p _ufs[index];_ufs[index] root;index p;}return root;}//将两个元素合拼到同一个集合里bool Union(int x1, int x2){int root1 FindRoot(x1);int root2 FindRoot(x2);if (root1 root2)return false;//小的并到大的里面if(abs(_ufs[root1]) abs(_ufs[root2]))swap(root1,root2);_ufs[root1] _ufs[root2];_ufs[root2] root1;return true;}// 数组中负数的个数即为集合的个数size_t Count()const{size_t ret 0;for (int i 0; i _ufs.size(); i){if (_ufs[i] 0)ret;}return ret;}//判断两个元素是否在同一个集合里bool IsGather(int x1, int x2){return FindRoot(x1) FindRoot(x2);}private:std::vectorint _ufs; };//W - 权值类型 templateclass W struct Edge {int _srci;int _dsti;W _w;Edge(int srci, int dsti, const W w): _srci(srci), _dsti(dsti), _w(w){}//重载大于bool operator( const EdgeW e) const{return _w e._w;} };//最小生成树 – Kruskal算法 W Kruskal(Self minTree) {//通过现有的图对minTree进行初始化int n _vertexs.size();minTree._vertexs _vertexs;minTree._vIndexMap _vIndexMap;minTree._matrix.resize(n);for (int i 0; i n; i){minTree._matrix[i].resize(n, MAX_W);}//使用优先队列保存所有边priority_queueEdge, vectorEdge, greaterEdge pq;for (int i 0; i n; i){for (int j i; j n; j){if (_matrix[i][j] ! MAX_W){pq.push(Edge(i, j, _matrix[i][j]));}}}//通过并查集来判环UnionFindSet ufs(n);//开始选边 – 选n-1条int count 0;//记录边的数W ret W();//记录权值while (!pq.empty()){//取队头元素Edge e pq.top();pq.pop();//只要两个点不同时在ufs中说明这条边可选if (!ufs.IsGather(e._srci, e._dsti)){//加入并查集ufs.Union(e._srci,e._dsti);//添加边minTree._AddEdge(e._srci,e._dsti,e._w);count;ret e._w;}//选完边结束if(count n-1)break;}return ret;}3、Prim算法 上图来自算法导论。 怎么实现 1从一个顶点开始将与其相连的边加入优先队列小的在队头。 2使用一个vector容器标记顶点是否被访问 3出队通过被指向的边是否被标记来判环该边的起点肯定已经被标记了因为该边进队时就是选择被标记的点作为起点的没被标记选择直到选择n-1n:顶点数条边。 //W - 权值类型 templateclass W struct Edge {int _srci;int _dsti;W _w;Edge(int srci, int dsti, const W w): _srci(srci), _dsti(dsti), _w(w){}//重载大于bool operator( const EdgeW e) const{return _w e._w;} }; //最小生成树 – Prim算法 W Prim(Self minTree, const V src) {//通过现有的图对minTree进行初始化int n _vertexs.size();minTree._vertexs _vertexs;minTree._vIndexMap _vIndexMap;minTree._matrix.resize(n);for (int i 0; i n; i){minTree._matrix[i].resize(n, MAX_W);}//标记点是否被选vectorint vis(n);int srci GetVertexIndex(src);vis[srci] true;//使用优先队列保存当前点出去的边priority_queueEdge, vectorEdge, greaterEdge pq;for (int i 0; i n; i){if (_matrix[srci][i] ! MAX_W){pq.push(Edge(srci, i, _matrix[srci][i]));}}W ret W();//记录权值int count 0;//记录当前边数while (!pq.empty()){//取出队头元素Edge e pq.top();pq.pop();//判环 –只要当前边的e._dsti没被标记就行因为该边是由e._srci点发出的所以//e._srci肯定被标志了不用判断if (vis[e._dsti] false){//修改标志vis[e._dsti] true;//添加边minTree._AddEdge(e._srci, e._dsti, e._w);//将新加入的点出去的边添加到优先队列中for (int i 0; i n; i){if (_matrix[e._dsti][i] ! MAX_W){pq.push(Edge(e._dsti, i, _matrix[e._dsti][i]));}}ret e._w;count;}//选完了结束if (count n - 1)break;}return ret; }五、最短路径问题 最短路径问题从在带权有向图G中的某一顶点出发找出一条通往另一顶点的最短路径最短也就是沿路径各边的权值总和达到最小。 1、单源最短路径–Dijkstra算法 单源最短路径问题给定一个图G ( V E ) G(VE)G(VE)求源结点s ∈ V s∈Vs∈V到图 中每个结点v ∈ V v∈Vv∈V的最短路径。Dijkstra算法就适用于解决带权重的有向图上的单源最短 路径问题同时算法要求图中所有边的权重非负。一般在求解最短路径的时候都是已知一个起点和一个终点所以使用Dijkstra算法求解过后也就得到了所需起点到终点的最短路径。针对一个带权有向图G将所有结点分为两组S和QS是已经确定最短路径的结点集合在初始时 为空初始时就可以将源节点s放入毕竟源节点到自己的代价是0Q 为其余未确定最短路径 的结点集合每次从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算法存在的问题是不支持图中带负权路径如果带有负权路径则可能会找不到一些路 径的最短路径。 上图来自算法导论。 //src – 开始的点 dist – src到每个点的最短距离 // parentPath – 记录src到其他点的过程上的最近顶点 如src - k - j j点记录的是k的下标 void Dijkstra(const V src, vectorW dist, vectorint parentPath) {//初始化int n _vertexs.size();dist.resize(n, MAX_W);parentPath.resize(n, -1);//标记数组vectorbool vis(n);//给dist数组srci位置赋最小值方便第一次找到int srci GetVertexIndex(src);dist[srci] W();//n个顶点更新n次for (int i 0; i n; i){int u 0;int min MAX_W;//到srci最小的点for(int j 0; j n; j){if (vis[j] false min dist[j]){u j;min dist[j];}}//标志vis[u] true;//松弛操作// 更新u-其他点srci-u-其他点的distfor (int k 0; k n; k){if (vis[k] false _matrix[u][k] ! MAX_W dist[k] dist[u] _matrix[u][k]){dist[k] dist[u] _matrix[u][k];parentPath[k] u;}}} }2、单源最短路径–Bellman-Ford算法 Dijkstra算法只能用来解决正权图的单源最短路径问题但有些题目会出现负权图。这时这个算法 就不能帮助我们解决问题了而bellman—ford算法可以解决负权图的单源最短路径问题。它的 优点是可以解决有负权边的单源最短路径问题而且可以用来判断是否有负权回路。它也有明显 的缺点它的时间复杂度 O(N*E) (N是点数E是边数)普遍是要高于Dijkstra算法O(N²)的。像这里 如果我们使用邻接矩阵实现那么遍历所有边的数量的时间复杂度就是O(N^3)这里也可以看出 来Bellman-Ford就是一种暴力求解更新 上图来自算法导论。 //src – 开始的点 dist – src到每个点的最短距离 // parentPath – 记录src到其他点的过程上的最近顶点 如src - k - j j点记录的是k的下标 bool BellmanFord(const V src, vectorW dist, vectorint parentPath) {size_t n _vertexs.size();size_t srci GetVertexIndex(src);// vectorW dist,记录srci-其他顶点最短路径权值数组dist.resize(n, MAX_W);// vectorint parentPath 记录srci-其他顶点最短路径父顶点数组parentPath.resize(n, -1);// 先更新srci-srci为最小值,方便第一次找到dist[srci] W();//最多更新n-1次最坏的情况就是到另一个点需要更新n-1次for (int k 0; k n-1; k){//标记如果没有修改则完成查找最短路径bool flag false;for (int i 0; i n; i){for (int j 0; j n; j){//找到更小的了进行修改if (_matrix[i][j] ! MAX_W dist[i] _matrix[i][j] dist[j]){cout _vertexs[i] - _vertexs[j] : _matrix[i][j] | ;dist[j] dist[i] _matrix[i][j];parentPath[j] i;flag true;}}}//全部都是最短路径了 - 结束if (flag false)break;}//检查有没有负权回路for (int i 0; i n; i){for (int j 0; j n; j){if (_matrix[i][j] ! MAX_W dist[i] _matrix[i][j] dist[j]){return false;}}}return true; }3、多源最短路径–Floyd-Warshall算法 Floyd-Warshall算法是解决任意两点间的最短路径的一种算法。 Floyd算法考虑的是一条最短路径的中间节点即简单路径p{v1,v2,…,vn}上除v1和vn的任意节点。设k是p的一个中间节点那么从i到j的最短路径p就被分成i到k和k到j的两段最短路径p1p2。p1是从i到k且中间节点属于{12…k-1}取得的一条最短路径。p2是从k到j且中间节点属于{12…k-1}取得的一条最短路径。 上图来自算法导论。 即Floyd算法本质是三维动态规划D[i][j][k]表示从点i到点j只经过0到k个点最短路径然后建立起转移方程然后通过空间优化优化掉最后一维度变成一个最短路径的迭代算法最后即得到所以点的最短路。 //vvDist – 记录全部的点到其他点的小权值 //vvParentPath – 记录全部点到其他点的过程上的最近顶点 如src - k - j j点记录的是k的下标 void FloydWarShall(vectorvectorW vvDist, vectorvectorintvvParentPath) {// 初始化size_t n _vertexs.size();vvDist.resize(n);vvParentPath.resize(n);//初始化权值和路径矩阵for (size_t i 0; i n; i){vvDist[i].resize(n, MAX_W);vvParentPath[i].resize(n, -1);}//将相连的边连在一起for (int i 0; i n; i){for (int j 0; j n; j){if (_matrix[i][j] ! MAX_W){vvDist[i][j] _matrix[i][j];vvParentPath[i][j] i;}else if (i j){vvDist[i][j] W();vvParentPath[i][j] -1;}}}//将k作为中转点依次遍历for (int k 0; k n; k){for (int i 0; i n; i){for (int j 0; j n; j){//i -( k )- jif (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];vvParentPath[i][j] vvParentPath[k][j];}}}}}六、拓扑排序 1、概念 拓扑排序是对有向无环图DAG顶点的一种排序。 在一个DAG中如果存在一条有向边(u, v)那么在拓扑排序的结果中顶点u会排在顶点v的前面。它主要用于解决任务调度、课程学习顺序等依赖关系问题就是找做事情的先后顺序通常可以用深度优先搜索DFS或 Kahn算法来实现拓扑排序。 注意拓扑排序的顺序不唯一。 2、如何排序 以下是使用 Kahn 算法实现拓扑排序的步骤 。 遍历有向图中的所有边对于每条边(u, v)将顶点 v 的入度加一。将入度为 0 的顶点加入队列。 初始化一个队列用于存储入度为 0 的顶点。遍历所有顶点将入度为 0 的顶点加入队列。进行拓扑排序。 创建一个空列表用于存储排序后的顶点。当队列不为空时从队列中取出一个顶点 u。将顶点 u 加入排序后的列表中。遍历顶点 u 的所有邻接顶点 v将顶点 v 的入度减一。如果顶点 v 的入度变为 0则将其加入队列。检查是否存在有向环。 如果排序后的列表中顶点的数量与图中的顶点数量相同则说明图中不存在有向环拓扑排序成功。否则说明图中存在有向环无法进行拓扑排序。 3、实现 //拓扑排序 //graph数组 – 下标指向一个vectorint,vectorint - 该下标顶点指向的顶点 //如0 - {1,2} ,在图中有两条边0102 vectorint topologicalSort(vectorvectorint graph) {//1、统计入度int n graph.size();vectorint in(n, 0);for (int i 0; i n; i){for (auto e : graph[i])in[e];}//2、将入度为0的进队列queueint q;for (int i 0; i n; i){if (in[i] 0)q.push(i);}//3、拓扑排序vectorint ret;while (!q.empty()){//出队int front q.front();q.pop();//加入ret.push_back(front);//将front指向的入度减1for (auto e : graph[front]){in[e]–;//再次统计入度为0的顶点if (in[e] 0)q.push(e);}}//4、判断是否存在有向环if (ret.size() n)return {};return ret; }4、应用 题目课程表 使用算法拓扑排序 这题多了一步就是需要我们自己建图遍历prerequisites使用vectorvector 或者mapint,vector将图建如顶点a指向其他顶点的集合出来其他步骤和上述代码差不多了。 代码实现 class Solution { public:bool canFinish(int numCourses, vectorvectorint prerequisites) {//1、建图vectorvectorint graph(numCourses);int n prerequisites.size();for(int i 0; i n; i){int a prerequisites[i][0];int b prerequisites[i][1];graph[b].push_back(a);}//统计入度vectorint in(numCourses, 0);for (int i 0; i numCourses; i){for (auto e : graph[i]){in[e];}}//3、将入度为0的进队列queueint q;for (int i 0; i numCourses; i){if (in[i] 0)q.push(i);}//4、拓扑排序while (!q.empty()){//出队int front q.front();q.pop();//将front指向的入度减1for (auto e : graph[front]){in[e]–;//再次统计入度为0的顶点if (in[e] 0)q.push(e);}}//4、判断是否存在有向环for(int i 0; i numCourses; i){if(in[i]) return false;}return true;} };
- 上一篇: 用php如何建设网站oa系统排名
- 下一篇: 用php做电商网站对亚马逊网站做简要分析与评价
相关文章
-
用php如何建设网站oa系统排名
用php如何建设网站oa系统排名
- 技术栈
- 2026年04月20日
-
用php开发网站教程南宁网站如何制作
用php开发网站教程南宁网站如何制作
- 技术栈
- 2026年04月20日
-
用phpcms v9搭建手机网站后您没有访问该信息的权限!网站logo设计标准
用phpcms v9搭建手机网站后您没有访问该信息的权限!网站logo设计标准
- 技术栈
- 2026年04月20日
-
用php做电商网站对亚马逊网站做简要分析与评价
用php做电商网站对亚马逊网站做简要分析与评价
- 技术栈
- 2026年04月20日
-
用php做企业网站的可行性网页设计与制作论文5000字
用php做企业网站的可行性网页设计与制作论文5000字
- 技术栈
- 2026年04月20日
-
用php做网站和go做网站做网站原型图用什么软件
用php做网站和go做网站做网站原型图用什么软件
- 技术栈
- 2026年04月20日
