# 0886. 可能的二分法 ## 题目地址(886. 可能的二分法) <https://leetcode-cn.com/problems/is-graph-bipartite/> ## 题目描述 ``` <pre class="calibre18">``` 给定一组 N 人(编号为 1, 2, ..., N), 我们想把每个人分进任意大小的两组。 每个人都可能不喜欢其他人,那么他们不应该属于同一组。 形式上,如果 dislikes[i] = [a, b],表示不允许将编号为 a 和 b 的人归入同一组。 当可以用这种方法将每个人分进两组时,返回 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 <= 10000 dislikes[i].length == 2 1 <= dislikes[i][j] <= N dislikes[i][0] < dislikes[i][1] 对于dislikes[i] == dislikes[j] 不存在 i != j ``` ``` ## 前置知识 - 图的遍历 - DFS ## 公司 - 暂无 ## 思路 这是一个图的问题。解决这种问题一般是要遍历图才行的,这也是图的套路。 那么遍历的话,你要有一个合适的数据结构。 比较常见的图存储方式是邻接矩阵和邻接表。 而我们这里为了操作方便(代码量),直接使用邻接矩阵。由于是互相不喜欢,不存在一个喜欢另一个,另一个不喜欢一个的情况,因此这是无向图。而无向图邻接矩阵实际上是会浪费空间,具体看我下方画的图。 而题目给我们的二维矩阵并不是现成的邻接矩阵形式,因此我们需要自己生成。 我们用 1 表示互相不喜欢(dislike each other)。 ``` <pre class="calibre18">``` graph = [[<span class="hljs-params">0</span>] * N <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(N)] <span class="hljs-keyword">for</span> a, b <span class="hljs-keyword">in</span> dislikes: graph[a - <span class="hljs-params">1</span>][b - <span class="hljs-params">1</span>] = <span class="hljs-params">1</span> graph[b - <span class="hljs-params">1</span>][a - <span class="hljs-params">1</span>] = <span class="hljs-params">1</span> ``` ``` ![](https://img.kancloud.cn/e4/de/e4de86de14a80d16572add8a19a09f5f_528x470.jpg) 同时可以用 hashmap 或者数组存储 N 个人的分组情况, 业界关于这种算法一般叫染色法,因此我们命名为 colors,其实对应的本题叫 groups 更合适。 ![](https://img.kancloud.cn/42/76/42766bfa4c33f66ae0e5abaf5043e98c_316x110.jpg) 我们用: - 0 表示没有分组 - 1 表示分组 1 - -1 表示分组 2 之所以用 0,1,-1,而不是 0,1,2 是因为我们会在不能分配某一组的时候尝试分另外一组,这个时候有其中一组转变为另外一组就可以直接乘以-1,而 0,1,2 这种就稍微麻烦一点而已。 具体算法: - 遍历每一个人,尝试给他们进行分组,比如默认分配组 1. ![](https://img.kancloud.cn/6d/57/6d57f8709c279659f35582e7df45f1a7_415x202.jpg) - 然后遍历这个人讨厌的人,尝试给他们分另外一组,如果不可以分配另外一组,则返回 False 那问题的关键在于如何判断“不可以分配另外一组”呢? ![](https://img.kancloud.cn/9e/7a/9e7a41cd1f78416c18e5bc860c2865c4_1421x733.jpg) 实际上,我们已经用 colors 记录了分组信息,对于每一个人如果分组确定了,我们就更新 colors,那么对于一个人如果分配了一个组,并且他讨厌的人也被分组之后,**分配的组和它只能是一组**,那么“就是不可以分配另外一组”。 代码表示就是: ``` <pre class="calibre18">``` <span class="hljs-title"># 其中j 表示当前是第几个人,N表示总人数。 dfs的功能就是根据colors和graph分配组,true表示可以分,false表示不可以,具体代码见代码区。</span> <span class="hljs-keyword">if</span> colors[j] == <span class="hljs-params">0</span> <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> self.dfs(graph, colors, j, <span class="hljs-params">-1</span> * color, N) ``` ``` ## 关键点 - 二分图 - 染色法 - 图的建立和遍历 - colors 数组 ## 代码 ``` <pre class="calibre18">``` <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Solution</span>:</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">dfs</span><span class="hljs-params">(self, graph, colors, i, color, N)</span>:</span> colors[i] = color <span class="hljs-keyword">for</span> j <span class="hljs-keyword">in</span> range(N): <span class="hljs-title"># dislike eachother</span> <span class="hljs-keyword">if</span> graph[i][j] == <span class="hljs-params">1</span>: <span class="hljs-keyword">if</span> colors[j] == color: <span class="hljs-keyword">return</span> <span class="hljs-keyword">False</span> <span class="hljs-keyword">if</span> colors[j] == <span class="hljs-params">0</span> <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> self.dfs(graph, colors, j, <span class="hljs-params">-1</span> * color, N): <span class="hljs-keyword">return</span> <span class="hljs-keyword">False</span> <span class="hljs-keyword">return</span> <span class="hljs-keyword">True</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">possibleBipartition</span><span class="hljs-params">(self, N: int, dislikes: List[List[int]])</span> -> bool:</span> graph = [[<span class="hljs-params">0</span>] * N <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(N)] colors = [<span class="hljs-params">0</span>] * N <span class="hljs-keyword">for</span> a, b <span class="hljs-keyword">in</span> dislikes: graph[a - <span class="hljs-params">1</span>][b - <span class="hljs-params">1</span>] = <span class="hljs-params">1</span> graph[b - <span class="hljs-params">1</span>][a - <span class="hljs-params">1</span>] = <span class="hljs-params">1</span> <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(N): <span class="hljs-keyword">if</span> colors[i] == <span class="hljs-params">0</span> <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> self.dfs(graph, colors, i, <span class="hljs-params">1</span>, N): <span class="hljs-keyword">return</span> <span class="hljs-keyword">False</span> <span class="hljs-keyword">return</span> <span class="hljs-keyword">True</span> ``` ``` **复杂度分析** - 时间复杂度:O(N2)O(N^2)O(N2) - 空间复杂度:O(N)O(N)O(N) ## 相关问题 - [785. 判断二分图](785.is-graph-bipartite.html) 更多题解可以访问我的 LeetCode 题解仓库:<https://github.com/azl397985856/leetcode> 。 目前已经 37K star 啦。 关注公众号力扣加加,努力用清晰直白的语言还原解题思路,并且有大量图解,手把手教你识别套路,高效刷题。