数据结构:二分图以及判定二分图

news2024/11/10 7:46:56

文章目录

  • 一、二分图的基本知识
    • 1、特性
    • 2、图示
    • 3、检查一个图是否为二分图
      • 3.1、着色的算法原理和思路
      • 3.2、算法示例:使用 BFS 检查二分图
      • 3.3、算法示例:使用 DFS 检查二分图
    • 4、应用
  • 二、例题
    • 1.LeetCode:785. 判断二分图
    • 2.Acwing:860. 染色法判定二分图

二分图(Bipartite Graph)是一种特殊的图,它的顶点集合可以划分为两个不相交的子集,使得每条边都连接这两个子集中的一个顶点和另一个顶点。换句话说,二分图中的所有边都只能在两个不同的子集之间。

染色法判定二分图虽然听起来很难,但实际上是一个很简单的算法,读完此文章,相信大家都很容易掌握。

一、二分图的基本知识

一个图 G = ( V , E ) G = (V, E) G=(V,E) 是二分图,当且仅当其顶点集 V V V 可以分割为两个不相交的子集 U U U W W W,使得图中的每条边 e e e 满足 e e e 连接 U U U W W W 中的一个顶点。图 G G G 是二分图,当且仅当存在一个划分 ( U , W ) (U, W) (U,W) 使得:

  1. U ∪ W = V U \cup W = V UW=V
  2. U ∩ W = ∅ U \cap W = \emptyset UW=
  3. ∀ ( u , w ) ∈ E , 其中  u ∈ U  且  w ∈ W \forall (u, w) \in E, \text{其中 } u \in U \text{ 且 } w \in W (u,w)E,其中 uU  wW

由定义可知:包含自环的一定不是二分图;重边当作一条边来看即可。

1、特性

  1. 没有奇环:一个图是二分图当且仅当它不包含奇数长度的环。也就是说,所有环的长度都是偶数。
  2. 两色定理:二分图可以用两种颜色对顶点进行着色,使得每条边的两个端点的颜色不同。这种两种颜色的着色也称为二部着色(Bipartite Coloring)
    在这里插入图片描述

2、图示

一个简单的二分图示例:

  U: {1, 2, 3}
  W: {4, 5}
  Edges: {(1, 4), (2, 4), (3, 5)}

  1 --- 4
  2 --- 4
  3 --- 5

在这个例子中,顶点集 U U U { 1 , 2 , 3 } \{1, 2, 3\} {1,2,3},顶点集 W W W { 4 , 5 } \{4, 5\} {4,5},所有边都连接 U U U W W W 中的一个顶点。

3、检查一个图是否为二分图

通常使用染色法(二部着色法)判定二分图可以使用广度优先搜索BFS或深度优先搜索DFS来检查一个图是否为二分图。通过对图进行着色,如果能用两种颜色对图进行着色,使得相邻顶点颜色不同,那么这个图就是二分图。否则,它不是二分图。

相当于将结点分为两个集合的方法转换成了着色问题。
在这里插入图片描述

3.1、着色的算法原理和思路

由于我们知道,对于 ∀ ( u , w ) ∈ E \forall (u, w) \in E (u,w)E,则必须有 u u u w w w属于不同的集合,那么相当于必须给 u u u w w w必须染上不同的颜色。由于二分图只能分为两个集合因此只有有两种颜色,那么我们可以使用普通的遍历方法进行交替着色。相同颜色表示在同一个集合中,它们之间必须没有边。

例如我们将0号结点初始化为红色,从0号结点开始进行遍历,对其相邻结点进行绿色着色,然后使用dfsbfs遍历策略,再选择其相邻节点进行遍历(注意已经访问过的结点不再次访问),在遍历的过程中,如果相邻结点已经被染色,且染色的颜色和其不一样,则出现错误,因为染过色的结点必然是受到之前遍历过的结点颜色限制的,此时与之前的限制矛盾,则必然不是二分图;如果染色的颜色和其一样,则不再考虑它。直至遍历完所有节点,如果遍历完了所有结点且不存在问题则是二分图。

  • 已经访问过的结点不再次访问的原因是:如果访问它,根据算法说明前面的染色都是正确的,再次访问它,它又进行相同的染色,产生了重复,会进入死循环
  • 如果染色的颜色和其一样,则不再考虑它的原因是:根据dfs一个节点一旦被染色则一定被深度遍历,被访问不能再次访问避免重复。根据bfs一个节点一旦被染色则一定被入队列,被入队列的节点即被访问。只有未被染色的结点才是从未被访问的结点。

可以发现,不连通图的多个连通分量,分别是二分图就行。一个顶点的图必然是二分图。

交替染色方法:
将颜色标记为01,未染色标记为-1,使用异或进行交替染色如下:

w_color = u_color ^ 1;//值得注意的是^运算 比 ==运算的优先级还低

如果u的颜色是0则异或为1,如是1则异或为0

使用的数据结构:
除了存储图之外,我们最好使用一个数组来存储各个顶点的颜色,以便于对不同连通分量进行访问。

3.2、算法示例:使用 BFS 检查二分图

#include <bits/stdc++.h>
using namespace std;

bool bfs(const vector<vector<int>>& graph, int start, vector<int>& color) {
    queue<int> q;
    q.push(start);
    color[start] = 0;

    while (!q.empty()) {
        int node = q.front();
        q.pop();

        for (int neighbor : graph[node]) {
            if (color[neighbor] == -1) {
                color[neighbor] = color[node] ^ 1; // 着不同的颜色
                q.push(neighbor);
            } else if (color[neighbor] == color[node]) {
                return false; // 相邻节点颜色相同,不是二分图
            }
        }
    }
    return true;
}

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

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);

    int n, m;
    cin >> n >> m;
    vector<vector<int>> graph(n);
    
    for (int i = 0; i < m; ++i) {
        int u, v;
        cin >> u >> v;
        --u; --v; // 将节点编号调整为从 0 开始
        graph[u].emplace_back(v);
        graph[v].emplace_back(u);
    }

    if (isBipartite(graph)) {
        cout << "Yes\n";
    } else {
        cout << "No\n";
    }

    return 0;
}

3.3、算法示例:使用 DFS 检查二分图

在这里插入图片描述

#include <bits/stdc++.h>
using namespace std;

vector<int> color;

bool dfs(const vector<vector<int>>& graph, int u, int c) {
    color[u] = c;
    for (int v : graph[u]) {
        if (color[v] == -1) {
            if (!dfs(graph, v, c ^ 1)) {
                return false;
            }
        } else if (color[v] == color[u]) {
            return false;
        }
    }
    return true;
}

bool isBipartite(const vector<vector<int>>& graph) {
    int n = graph.size();
    color.assign(n, -1);
    
    for (int i = 0; i < n; ++i) {
        if (color[i] == -1) {
            if (!dfs(graph, i, 0)) {
                return false;
            }
        }
    }
    return true;
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);

    int n, m;
    cin >> n >> m;
    vector<vector<int>> graph(n);
    
    for (int i = 0; i < m; ++i) {
        int u, v;
        cin >> u >> v;
        --u; --v; // 将节点编号调整为从 0 开始
        graph[u].emplace_back(v);
        graph[v].emplace_back(u);
    }

    if (isBipartite(graph)) {
        cout << "Yes\n";
    } else {
        cout << "No\n";
    }

    return 0;
}

4、应用

二分图在许多应用中具有重要意义,例如:

  • 匹配问题:如二分图最大匹配,用于分配任务或资源。
  • 网络流:二分图匹配可以转换为最大流问题。
  • 推荐系统:用户与物品的关系图可以建模为二分图。

二、例题

1.LeetCode:785. 判断二分图

在这里插入图片描述
读了题干可知,这是一个经典的判定二分图的题目,直接使用基本方法即可。这里使用DFS。

class Solution {
public:
    bool isBipartite(vector<vector<int>>& graph) {
        color.assign(graph.size(), -1);
        for(int i = 0; i < graph.size(); ++ i){
            if(color[i] == -1){
                color[i] = 0;
                if(!Bfs(graph, i)){
                    return false;
                }
            }
        }
        return true;
    }
private:
    bool Bfs(vector<vector<int>> & graph, int u){
        queue<int> q;
        q.push(u);
        while(!q.empty()){
            int s = q.front();q.pop();
            for(auto & v : graph[s]){
                if(color[v] == color[s]) return false;
                if(color[v] == (color[s] ^ 1)) continue;
                if(color[v] == -1){
                    color[v] = color[s] ^ 1;
                    if(!Bfs(graph, v)) return false;
                }
            }
        }
        return true;
    }
    vector<int> color;
};

2.Acwing:860. 染色法判定二分图

在这里插入图片描述
虽然这个题存在重边和自环,但完全不影响判断。

  • 自环也是边,按相同的方式进行着色,会发现一定不是二分图。
  • 重边也是边,按相同的方式进行着色,两次判断一个相连顶点不会存在两次颜色不一样,而且因为已经着色的点不会再次被访问也不会出现重复遍历的情况。

这里使用BFS和DFS都行,为了和上题区分,这里使用了DFS:

#include<bits/stdc++.h>
using namespace std;
class Solution {
public:
    bool isBipartite(vector<vector<int>>& graph) {
        color.assign(graph.size(), -1);
        for(int i = 0; i < graph.size(); ++ i){
            if(color[i] == -1){
                color[i] = 0;
                if(!dfs(graph, i)){
                    return false;
                }
            }
        }
        return true;
    }
private:
    bool dfs(vector<vector<int>> & graph, int u){
        for(auto & v : graph[u]){//u的边集
            if(color[v] == color[u]) return false;//相邻顶点和u颜色相同
            if(color[v] == (color[u] ^ 1)) continue;//相邻顶点和u颜色不相同,直接结束
            if(color[v] == -1){
                color[v] = color[u] ^ 1;
                if(!dfs(graph, v)) return false;
            }
        }
        return true;
    }
    vector<int> color;
};
int main(void){
    ios_base::sync_with_stdio(false);
    cin.tie(0);
    int n, m;
    cin >> n >> m;
    vector<vector<int>> graph(n);
    while(m --){
        int u, v; cin >> u >> v;
        graph[--u].emplace_back(--v);
        graph[v].emplace_back(u);
    }
    Solution Bipartite;
    if(Bipartite.isBipartite(graph)){
        cout << "Yes";   
    }else cout << "No";
    return 0;
}

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

软件开发中UML的基本概念与UML类图中存在哪些关系与如何绘制?

UML的概述 UML在维基百科中是这样子描述的 可以使用的工具这里网上说的有些花哨,我自己一般使用processon,里面的UML相关功能就很全。 UML类图的相关概念解释 解释一下什么是类图? 定义系统中的类,描述系统中类的属性和方法,就是描述类的内部结构,表示类与类之间的关…

手摸手教你撕碎西门子S7通讯协议04--S7COMM请求

1、S7通讯回顾 - &#xff08;1&#xff09;建立TCP连接 Socket.Connect-》已实现 - &#xff08;2&#xff09;发送访问请求 COTP-》已实现 - &#xff08;3&#xff09;交换通信信息 Setup Communication-》本节实现 - &#xff08;4&#xff09;执行相关操作 …

诱骗IoT恶意软件跟踪CC服务器

工作背景 在分析 IoT 僵尸网络时&#xff0c;识别C&C 服务器至关重要。C&C 服务器的 IP 地址一直都是商业威胁情报的重要组成部分&#xff0c;由于 C&C 服务器通信协议日渐复杂并且活跃周期较短&#xff0c;时效性和准确性也非常重要。如果可以自动化识别 IoT 恶意…

深度学习实战笔记3循环神经网络实现

我们要训练一个基于循环神经网络的字符级语言模型&#xff0c;根据用户提供的文本的前缀生成后续文本。 import math import torch from torch import nn from torch.nn import functional as F from d2l import torch as d2l batch_size, num_steps 32, 35 train_iter, voc…

防震减灾知识竞赛的规则和流程方案

防震减灾知识竞赛的规则主要包括赛制、比赛形式、参赛对象、竞赛内容等方面。 赛制&#xff1a;通常包括选拔赛、分区预赛和全国决赛三个阶段。选拔赛可以根据地区实际情况选择合适的组织形式&#xff0c;预赛和决赛则以现场比赛形式进行&#xff0c;由主办单位统一组织。 比…

掌握 Spring Boot + MyBatis-Plus 动态数据源切换,只要5分钟!

数据量猛增&#xff0c;通过动态数据源切换&#xff0c;我们不仅能提高查询效率&#xff0c;还能保证系统的高可用性。 通过将写操作集中在主库&#xff0c;读操作分散到多个从库&#xff0c;可以有效减轻数据库的压力。 在pom.xml中添加以下依赖&#xff1a; xml <depend…

记录|LabVIEW从0开始

目录 前言一、表达式节点和公式节点二、脚本与公式2.1 公式 三、Excel表格3.1 位置3.2 案例&#xff1a;波形值存入Excel表中3.3 案例&#xff1a;行写入&#xff0c;列写入 四、时间格式化4.1 获取当前时间4.2 对当前时间进行格式化 更新时间 前言 参考视频&#xff1a; LabVI…

hadoop学习(二)

一.MapReduce 1.1定义&#xff1a;是一个分布式运算程序的编程框架 1.2核心功能&#xff1a;将用户编写的业务逻辑代码和自带默认组件整合成一个完整的分布式运算程序&#xff0c;并发运行在一个Hadoop集群上。 1.3优点 1&#xff09;易于编程 它简单的实现一些接口&#…

从入门到精通:电商设计师的职业发展指南

在当今数字时代&#xff0c;电商设计师的作用越来越重要。从电子商务网站的整体造型设计到产品页面的具体布局&#xff0c;他们的工作范围是电子商务企业成功的关键因素之一。然而&#xff0c;并不是每个人都对这个职业有深刻的理解。因此&#xff0c;在本文中&#xff0c;我们…

web3d值得学习并长期发展,性价比高吗?

在数字化浪潮日益汹涌的今天&#xff0c;Web3D技术以其独特的魅力和广泛的应用前景&#xff0c;逐渐成为技术领域的焦点。对于许多热衷于技术探索和创新的人来说&#xff0c;学习并长期发展Web3D技术无疑是一个值得考虑的选择。那么&#xff0c;Web3D技术的学习和发展究竟是否性…

华为openEuler 24.03 LTS系统安装系统后用SSH工具不能连接

sudo dnf install vim #安装VIM vim /etc/ssh/sshd_config #修改文件如图systemctl restart sshd #重启服务再用SSH工具连接成功

【树莓派+OpenCV+STM32】智能小车巡线_提取线路数据并通过串口通信传输

一、所用材料 树莓派4B树莓派官方摄像头STM32F103C8T6最小系统板 二、实现功能 在树莓派上用OpenCV对摄像头中的图像进行处理&#xff0c;将图像处理后的数据通过串口通信给到下位机STM32F103C8T6&#xff0c;再由下位机给出控制信号&#xff0c;利用pid算法实现对小车运动轨…

备忘录系统

目录 一、 系统简介 1.简介 2需求分析 3 编程环境与工具 二、 系统总体设计 1 系统的功能模块图。 2 各功能模块简介 3项目结构 4 三、 主要业务流程 &#xff08;1&#xff09;用户及管理员登录流程图 &#xff08;2&#xff09;信息添加流程 &#xff0…

强烈推荐这三款IOS应用,让你的生活更美好

Dino记账 Dino记账是一款结合了简洁设计和强大功能的记账应用&#xff0c;它通过多维度图表帮助用户轻松掌握金钱流向。应用界面明亮且配色突出&#xff0c;使得记录内容易于阅读&#xff0c;让记账和管理账目变得更加简单。 主要特性&#xff1a; 极简风格与易用性&#xff1…

史上最全Spring的@Transactional不生效的12大场景

一、事务不生效 1、访问权限的问题 在Spring框架中&#xff0c;AbstractFallbackTransactionAttributeSource是用于确定一个给定的方法是否应该被事务管理的一个抽象类。它的computeTransactionAttribute方法用于计算并返回一个方法的TransactionAttribute。computeTransacti…

SD NAND存储卡:小尺寸下的大容量存储

SD NAND是一种基于NAND闪存技术的存储设备&#xff0c;采用SD卡形式&#xff0c;具备高存储容量、高速度和高可靠性的特点&#xff0c;广泛应用于嵌入式系统和消费电子产品中。 在如今数据驱动的世界&#xff0c;存储技术的发展至关重要。MK米客方德作为存储芯片行业的领先者&…

C++(week14): C++提高:(一)面向对象设计:设计原则、设计模式

文章目录 一、面向对象设计的概念4.统一建模语言&#xff1a;UML语言StartUML 二、类与类之间的关系0.总结(1)类与类的五种关系(2)区别(3)面向对象 vs 基于对象 1.继承 (泛化耦合)2.组合 (Composition)3.聚合 (Aggregation)4.关联(1)双向关联(2)单向关联 5.依赖 (Dependency) 三…

JNDI注入-RMI和Reference

参考博客&#xff1a; JNDI注入与动态类加载 JNDI Java命名和接口目录为用Java编程语言编写的应用程序提供命名和目录功能。 可以通过一种通用方式访问各种服务&#xff0c;类似通过名字查找对象的功能&#xff0c;和RMI有点类似。 原生JNDI支持RMI&#xff0c;LDAP&#…

2024最新前端学习路线指南!

2024最新前端学习路线指南&#xff01; 如果你正在寻找一份全面的前端学习路线图&#xff0c;那么这份精心打造的学习大纲恰好符合您的需求。无论您是新手还是经验丰富的开发者&#xff0c;这份路线图都能够帮助您系统地掌握前端开发的关键知识点&#xff0c;并在实践中不断提…

实验2-3-7 阶梯电价

//实验2-3-7 阶梯电价 /*为了提倡居民节约用电&#xff0c;某省电力公司执行“阶梯电价”&#xff0c; 安装一户一表的居民用户电价分为两个“阶梯”&#xff1a; 月用电量50千瓦时&#xff08;含50千瓦时&#xff09;以内的&#xff0c;电价为0.53元/千瓦时&#xff1b; 超过5…