LeetCode 0886. 可能的二分法

【LetMeFly】886.可能的二分法:图搜索

力扣题目链接:https://leetcode.cn/problems/possible-bipartition/

给定一组 n 人(编号为 1, 2, ..., n), 我们想把每个人分进任意大小的两组。每个人都可能不喜欢其他人,那么他们不应该属于同一组。

给定整数 n 和数组 dislikes ,其中 dislikes[i] = [ai, bi] ,表示不允许将编号为 ai 和  bi的人归入同一组。当可以用这种方法将所有人分进两组时,返回 true;否则返回 false

 

    示例 1:

    输入:n = 4, dislikes = [[1,2],[1,3],[2,4]]
    输出:true
    解释:group1 [1,4], group2 [2,3]
    

    示例 2:

    输入:n = 3, dislikes = [[1,2],[1,3],[2,3]]
    输出:false
    

    示例 3:

    输入:n = 5, dislikes = [[1,2],[2,3],[3,4],[4,5],[1,5]]
    输出:false
    

     

    提示:

    • 1 <= n <= 2000
    • 0 <= dislikes.length <= 104
    • dislikes[i].length == 2
    • 1 <= dislikes[i][j] <= n
    • ai < bi
    • dislikes 中每一组都 不同

     

    方法一:广度优先搜索

    可以把题目理解为: n n n个节点的无向图,其中 d i s l i k e s dislikes dislikes为边

    这样,我们就能很容易地把图构建出来:

    vector<vector<int>> graph(n + 1);
    for (auto& v : dislikes) {
        graph[v[0]].push_back(v[1]);
        graph[v[1]].push_back(v[0]);
    }
    

    图构建完毕后,对图进行搜索。

    因为图可能不连通,因此当发现一个新的图的节点时,用哈希表记录下这个连通子图中的节点。

    这样,在图遍历过程中,我们就可以很容易地知道是否出现了“环”

    遍历过程中,我们给节点编号(分组),编号只有“1”和“2”

    如果某个节点的相邻节点已有编号,就看这两个节点的编号是否相同。

    若相邻两个节点编号相同,则分组失败,返回false

    若图成功遍历完毕,则返回true

    • 时间复杂度 O ( n + m ) O(n + m) O(n+m),其中 n n n是节点个数, m m m是边的个数
    • 空间复杂度 O ( n + m ) O(n + m) O(n+m)

    AC代码

    C++
    class Solution {
    public:
        bool possibleBipartition(int n, vector<vector<int>>& dislikes) {
    		// 建图
            vector<vector<int>> graph(n + 1);
            for (auto& v : dislikes) {
                graph[v[0]].push_back(v[1]);
                graph[v[1]].push_back(v[0]);
            }
    		// 遍历
            vector<bool> visited(n + 1, false);  // visited[i]表示节点i是否被遍历过
            vector<int> node(n + 1);  // node[i]表示节点i的编号
            for (int i = 1; i <= n; i++) {  // 查看每一个节点
                if (!visited[i]) {  // 若是一个未遍历过的新节点,则说明这是一个新的“子图”的节点
                    visited[i] = true;  // 标记为遍历过
                    node[i] = 1;  // 赋编号
                    unordered_set<int> appeared;  // 用来记录这个子图中都有哪些节点
                    appeared.insert(i);
                    queue<int> q;  // 广搜队列
                    q.push(i);
                    while (q.size()) {
                        int thisNode = q.front();  // 取出节点
                        q.pop();
                        for (int toNode : graph[thisNode]) {  // 节点临边
                            if (!visited[toNode]) {  // 第一次遍历到
                                visited[toNode] = true;
                                node[toNode] = node[thisNode] == 1 ? 0 : 1;
                                appeared.insert(toNode);
                                q.push(toNode);
                            }
                            else {
                                if (appeared.count(toNode)) {  // 这俩点相连
                                    if (node[thisNode] == node[toNode])
                                        return false;  // !!!!!!!!
                                }
                            }
                        }
                    }
                }
            }
            return true;
        }
    };
    

    同步发文于CSDN,原创不易,转载请附上原文链接哦~
    Tisfy:https://letmefly.blog.csdn.net/article/details/127344544

    评论 3
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包

    打赏作者

    Tisfy

    你的鼓励将是我创作的最大动力

    ¥1 ¥2 ¥4 ¥6 ¥10 ¥20
    扫码支付:¥1
    获取中
    扫码支付

    您的余额不足,请更换扫码支付或充值

    打赏作者

    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

    余额充值