ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

刷题笔记 -- 图论

2021-10-31 16:34:23  阅读:175  来源: 互联网

标签:图论 -- graph int colors vector 顶点 节点 刷题


1 Floyd 算法可以求出任意两点的最短距离

Floyd 算法是一个经典的动态规划算法。用通俗的语言来描述的话,首先我们的目标是寻找从点 i 到点 j 的最短路径。

从任意节点 i 到任意节点 j 的最短路径不外乎 2 种可能:

  1. 是直接从 i 到 j
  2. 是从 i 经过若干个节点 k 到 j

所以,我们假设 Dis(i,j) 为节点 u 到节点 v 的最短路径的距离,对于每一个节点 k,我们检查Dis(i,k) + Dis(k,j) < Dis(i,j) 是否成立,如果成立,证明从 i 到 k 再到 j 的路径比 i 直接到 j 的路径短,我们便设置 Dis(i,j) = Dis(i,k) + Dis(k,j),这样一来,当我们遍历完所有节点 k,Dis(i,j) 中记录的便是 i 到 j 的最短路径的距离。

时间复杂度: O(n^3)

这里需要注意的是最外层循环的是中间点 k

1
2
3
4
5
6
7
8
9
for(int k=0; k<n; k++) { 
    for(i=0; i<n; i++) {
         for(j=0; j<n; j++)
             if(A[i][j]>(A[i][k]+A[k][j])) {
                  A[i][j]=A[i][k]+A[k][j];
                  path[i][j]=k;
             } 
    }
}

2 Dijkstra(迪杰斯特拉)算法是典型的单源最短路径算法,用于计算一个节点到其他所有节点的最短路径。主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。Dijkstra算法是很有代表性的最短路径算法,注意该算法要求图中不存在负权边

问题描述:在无向图 G=(V,E) 中,假设每条边 E[i] 的长度为 w[i],找到由顶点 V0 到其余各点的最短路径。(单源最短路径)

2.算法描述

1)算法思想:设G=(V,E)是一个带权有向图,把图中顶点集合V分成两组,第一组为已求出最短路径的顶点集合(用S表示,初始时S中只有一个源点,以后每求得一条最短路径 , 就将加入到集合S中,直到全部顶点都加入到S中,算法就结束了),第二组为其余未确定最短路径的顶点集合(用U表示),按最短路径长度的递增次序依次把第二组的顶点加入S中。在加入的过程中,总保持从源点v到S中各顶点的最短路径长度不大于从源点v到U中任何顶点的最短路径长度。此外,每个顶点对应一个距离,S中的顶点的距离就是从v到此顶点的最短路径长度,U中的顶点的距离,是从v到此顶点只包括S中的顶点为中间顶点的当前最短路径长度。

2)算法步骤:

a.初始时,S只包含源点,即S={v},v的距离为0。U包含除v外的其他顶点,即:U={其余顶点},若v与U中顶点u有边,则<u,v>正常有权值,若u不是v的出边邻接点,则<u,v>权值为∞。

b.从U中选取一个距离v最小的顶点k,把k,加入S中(该选定的距离就是v到k的最短路径长度)。

c.以k为新考虑的中间点,修改U中各顶点的距离;若从源点v到顶点u的距离(经过顶点k)比原来距离(不经过顶点k)短,则修改顶点u的距离值,修改后的距离值的顶点k的距离加上边上的权。

d.重复步骤b和c直到所有顶点都包含在S中。

3 最小生成树 

class Solution {
public:
    int miniSpanningTree(int n, int m, vector<vector<int> >& cost) {
        // write code here
        vector<int> sets(n+1, -1);  
        for(int i=0;i<n+1;i++)
          sets[i]=i;
        
        std::sort(cost.begin(),cost.end(),            // 按照边从小到大排序
            [&](const vector<int>& v1, const vector<int>& v2) {
                return v1[2] <v2[2];
            });
        int res = 0;
        for(vector<int>& l : cost) {
            int u = l[0], v = l[1], c = l[2];
            int uR = findRoot(sets, u);
            int vR = findRoot(sets, v);
            if (uR != vR) {
                res += c;
                sets[vR] = uR;
                sets[v] = uR;
            }
        }
        return res;
    }

    int findRoot(vector<int>& sets, int n) {
        while(n!= sets[n]) {
            n = sets[n];
        }
        return n;
    }
};

4 二分图:


二分图的节点可以分成两种类型,任意一条边的两个节点属于不同类型,可以为图中各个节点着色,两种类型的节点就涂成两种颜色。如果图中任何一条边的两个节点都可以被涂成不同的颜色,则该图就为二分图。

一个图可能包含多个子图,需要逐次对每个子图涂色。需要一个数组 colors 标记所有节点的颜色,规定 -1 表示当前未涂色,0 表示第一种颜色,1 表示第 2 种颜色。为了给所有的节点着色,需要遍历图内的所有结点,在着色的过程中若碰到已着色,但是不符合一条边两个节点不同颜色的要求,即可判断该图不可能是二分图。遍历图的所有结点可以使用两种方式,即广度优先搜索和深度优先搜索。

广度优先搜索
基于队列实现的广度优先搜索算法如下:


class Solution {
private: 
    // BFS
    bool setColor(vector<vector<int>>& graph, vector<int>& colors, int i, int color) {
        colors[i] = color;
        queue<int> que;
        que.push(i);

        while (!que.empty()) {
            int v = que.front();
            que.pop();
            
            for (auto& neb : graph[v]) {
                // 已着色,但是不符合一条边两个节点不同颜色的要求
                if (colors[neb] >= 0) {
                    if (colors[neb] == colors[v]) {
                        return false;
                    }
                }
                // 未着色,按照要求着色
                else {
                    que.push(neb);
                    colors[neb] = 1 - colors[v];
                }
            }
        }
        return true;
    }

public:
    bool isBipartite(vector<vector<int>>& graph) {
        vector<int> colors(graph.size(), -1);

        for (int i = 0; i < graph.size(); ++i) {
            if (colors[i] == -1) {
                if (!setColor(graph, colors, i, 0)) {
                    return false;
                }
            }
        }
        return true;
    }
};


深度优先搜索
基于递归函数实现的深度优先搜索算法如下:


class Solution {
private: 
    // DFS
    bool setColor(vector<vector<int>>& graph, vector<int>& colors, int i, int color) {
        // 未着色,按照要求着色
        if (colors[i] >= 0) {
            return colors[i] == color;
        }
        colors[i] = color;
        for (auto& neb : graph[i]) {
            // 已着色,但是不符合一条边两个节点不同颜色的要求
            if (!setColor(graph, colors, neb, 1 - color)) {
                return false;
            }
        }
        return true;
    }

public:
    bool isBipartite(vector<vector<int>>& graph) {
        vector<int> colors(graph.size(), -1);

        for (int i = 0; i < graph.size(); ++i) {
            if (colors[i] == -1) {
                if (!setColor(graph, colors, i, 0)) {
                    return false;
                }
            }
        }
        return true;
    }
};

 

这里使用递归方法时, 不需要考虑 color =1 的情况, 因为dfs本质上是根据一串依赖链进行遍历,一旦依赖链上出现了矛盾, 就一定是错误的。 因此当给一个未定的节点 染 0 或 染1 时,不会出现什么区别。

标签:图论,--,graph,int,colors,vector,顶点,节点,刷题
来源: https://blog.csdn.net/hang_ning/article/details/121062506

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有