886. 可能的二分法「二分图」「DFS」「并查集」

思路

n个节点能不能分为两个集合,每个集合中任意两个节点不存在边相连,二分图问题。

二分图 + 判断奇圈

第一时间想到了图论中判断二分图的方法:图中是否存在奇圈。
以下判断奇圈的方法写的有点粗糙,若有更好的方法希望各位大佬可以分享一下让弟弟学习学习。

第一步:建图:
n个结点,边集合为dislikes数组中的元素,每个元素中两个结点不能分到同一个组中,因此看做一条无向边,采用邻接表双向建图。

第二步:利用DFS判断奇圈。
建完图后,该图中可能存在多个连通块,因此需要遍历所有节点来判断每个连通块内是否存在奇圈,每个连通块有唯一的一个入口节点,即为遍历节点时该连通块内第一个被遍历到的点。

我们以其中的一个连通块为例子:如下图

邻接表双向建图可得:

1: 2,4
2: 1,3
3: 2,4,7
4: 1,3,5,7
5: 4,6
6: 5,7
7: 3,4,6

由于需要额外判断奇圈,所以只利用visit数组来标记某个节点是否已经被访问过了是不够的。

本方法中,利用到几个辅助的数组以及变量来判断图中是否有奇圈:

  • cur:表示DFS递归过程中地轨道的节点编号;
  • prev:表示当前节点是遍历到prev后向下递归的;或者说是在DFS过程中,prev节点是当前节点的上一层节点(父节点)。该变量防止当前节点重新进入prev节点;
  • visit[]:标记数组,用于标记节点是否被访问过,是判断存在圈的关键;
  • distance[]:距离数组,用于记录当前连通块内从入口节点到某节点间的距离。需要注意的是这里的距离是DFS的距离,相当于DFS深度,而不是计算的最短距离。

综上,DFS的方法签名如下:

bool dfs(vector<vector<int>>& graph, int cur, int prev, int length, bool visit[], int distance[]);
  1. 邻接表双向建图,得到graph
  2. 初始化visit数组,使其元素全部为false;初始化distance数组,使其元素全部为0
  3. 遍历节点i,若visit[i] == false,则作为它所在连通块的入口节点,进入DFS过程,得到返回值ret,若ret==false则直接返回false,否则继续遍历节点。所有节点均遍历完成后均没有返回,则说明不存在奇圈,可返回true
  4. DFS过程:
    1. 首先是递归退出条件:visit[cur] == true,说明递归时遇到了已经访问过的结点,构成了圈,需要判断该圈的长度是否为奇数:return (length - distance[cur]) % 2 == 0;
    2. 没有进入上述判断,令visit[cur] = truedistance[cur] = length。(这里distance[cur]就记录了从入口节点i到当前节点cur的深度)
    3. 遍历节点cur的邻接点,固定节点v:
      1. v == prev,则去遍历下一个节点,防止一条边误判成圈情况;否则进入下一步。(这里不能利用visit[v]来判断是否继续进入DFS过程,否则会导致递归退出条件无法进入,从而无法判圈)
      2. 继续往下层遍历:dfs(graph, v, cur, length+1, visit, distance),若结果为false,直接返回false
      3. 所有邻接节点遍历结束均没有返回的话,说明当前连通块不存在奇圈,返回true

以图示为例介绍算法执行过程:

代码如下:

class Solution {
   
public:
    bool possibleBipartition(int n, vector<vector<int>>& dislikes) {
   
        //初始化无向图, 两边相连说明不能在同一个组中
        vector<vector<int>> graph(n+1, vector<int>());
        for (const auto &dislike : dislikes){
   
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值