算法分析week3 | 785. Is Graph Bipartite?

目录

  1. 题目描述
  2. 解题过程
  3. 完整代码
  4. 改进
  5. 改进代码

题目描述

3.7


解题过程

这道题与书上习题3.7类似。二部图是这样的图G=(V,E),其顶点集合可以被划分为两个子集(V = V1 ∪ V2 且 V1 ∩ V2 = Ф),并且子集内部的顶点之间没有边相连。

要判断一个无向图是否是二部图,我们可以考虑这个图是否可以用两种颜色为它着色,有边相连的两个顶点需要着不同颜色。

首先,用一个数组subsets来记录每个顶点的颜色,-1表示未着色,而0,1分别表示两种不同的颜色。

        vector<int> subsets;
        for(int i = 0; i < graph.size(); i++) subsets.push_back(-1);

由于给定的图可能由多个强连通部件构成,因此需要对每个顶点进行检查。如果当前顶点已经着色,则可以跳过,因为这表示该顶点所在的强连通部件已经被着色了。如果有一个强连通部件不能用两种颜色着色,则返回false,如果所有强连通部件都可以用两种颜色着色,则返回true。

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

explore使用深度优先遍历,对一个强连通部件进行着色。如果该部件不能用两种颜色着色,则返回false。

    bool explore(vector<int>& subsets, vector<vector<int>>& graph, int i) {
        if(subsets[i] == -1) subsets[i] = 0;
        for(int j = 0; j < graph[i].size(); j++) {
            if(subsets[graph[i][j]] != -1) {
                if(subsets[graph[i][j]] == subsets[i]) return false;
                continue;
            }
            subsets[graph[i][j]] = !subsets[i];
            if(graph[graph[i][j]].size() > 0) {
                if(!explore(subsets, graph, graph[i][j])) return false;
            }
        }
        return true;
    }

这是个线性时间算法,复杂度为O(V+E)


完整代码

class Solution {
public:
    bool isBipartite(vector<vector<int>>& graph) {
        vector<int> subsets;
        for(int i = 0; i < graph.size(); i++) subsets.push_back(-1);
        for(int i = 0; i < graph.size(); i++) {
            if(subsets[i] != -1) continue;
            if(!explore(subsets, graph, i)) return false;
        }
        return true;
    }
    bool explore(vector<int>& subsets, vector<vector<int>>& graph, int i) {
        if(subsets[i] == -1) subsets[i] = 0;
        for(int j = 0; j < graph[i].size(); j++) {
            if(subsets[graph[i][j]] != -1) {
                if(subsets[graph[i][j]] == subsets[i]) return false;
                continue;
            }
            subsets[graph[i][j]] = !subsets[i];
            if(graph[graph[i][j]].size() > 0) {
                if(!explore(subsets, graph, graph[i][j])) return false;
            }
        }
        return true;
    }
};

改进

O(V+E)已经是最快的算法了,但是我们还可以对之前的代码进行简化,使代码更简洁易懂

这次改进,直接将subsets声明为一个大小为graph.size(),即顶点数的数组,0表示为着色,而1,-1分别表示两种不同的颜色。由于如果当前顶点已经着色,explore在一开始就会返回,因此可以不用在for循环中判断该顶点是否着色。

    bool isBipartite(vector<vector<int>>& graph) {
        vector<int> subsets(graph.size());
        for(int i = 0; i < graph.size(); i++) {
            if(subsets[i] == 0 && !explore(subsets, graph, i, 1)) return false;
        }
        return true;
    }

explore增加了参数color,如果当前顶点已着色,则说明有环,只需检验当前顶点是否符合规定,否则会陷入死循环。如果没有着色,则给该顶点着色,并且分别检查与该顶点相连的其它顶点。

    bool explore(vector<int>& subsets, vector<vector<int>>& graph, int i, int color) {
        if(subsets[i] != 0) return subsets[i] == color;
        subsets[i] = color;
        for(auto node : graph[i]) {
            if(!explore(subsets, graph, node, -1 * color)) return false;
        }
        return true;
    }

第二种算法与第一种算法有略微的不同,主要是,第二种算法的思路是先规定当前顶点应该有的颜色,然后来检验当前顶点是否符合规定,而第一种则是在exlpore中进行着色,然后检验其后续顶点是否符合规定。当然,复杂度都是O(V + E)


改进代码

class Solution {
public:
    bool isBipartite(vector<vector<int>>& graph) {
        vector<int> subsets(graph.size());
        for(int i = 0; i < graph.size(); i++) {
            if(!explore(subsets, graph, i, 1)) return false;
        }
        return true;
    }
    bool explore(vector<int>& subsets, vector<vector<int>>& graph, int i, int color) {
        if(subsets[i] != 0) return subsets[i] == color;
        subsets[i] = color;
        for(auto node : graph[i]) {
            if(!explore(subsets, graph, node, -1 * color)) return false;
        }
        return true;
    }
};

猜你喜欢

转载自blog.csdn.net/cat_xing/article/details/82825056