leetcode-785. 判断二分图

题目

给定一个无向图graph,当这个图为二分图时返回true。

如果我们能将一个图的节点集合分割成两个独立的子集A和B,并使图中的每一条边的两个节点一个来自A集合,一个来自B集合,我们就将这个图称为二分图。

graph将会以邻接表方式给出,graph[i]表示图中与节点i相连的所有节点。每个节点都是一个在0到graph.length-1之间的整数。这图中没有自环和平行边: graph[i] 中不存在i,并且graph[i]中没有重复的值。

示例 1:
输入: [[1,3], [0,2], [1,3], [0,2]]
输出: true
解释: 
无向图如下:
0----1
|    |
|    |
3----2
我们可以将节点分成两组: {0, 2} 和 {1, 3}。

示例 2:
输入: [[1,2,3], [0,2], [0,1,3], [0,2]]
输出: false
解释: 
无向图如下:
0----1
| \  |
|  \ |
3----2
我们不能将节点分割成两个独立的子集。

注意:

graph 的长度范围为 [1, 100]。
graph[i] 中的元素的范围为 [0, graph.length - 1]。
graph[i] 不会包含 i 或者有重复的值。
图是无向的: 如果j 在 graph[i]里边, 那么 i 也会在 graph[j]里边。

解题思路

BFS/DFS

一开始没什么思路,大概瞟了一眼题解,然后做出来的。

深度优先搜索,每次访问到1个节点时,把节点周围的邻居节点压入栈,并且根据该节点的颜色,给邻居节点上其他色。如果栈pop出的节点已经被访问过,则查看节点被涂的色和在栈内备选的色是否相同,如果不同,就说明有节点冲突了

需要注意的是,图可能是不联通的,从1个节点可能无法访问到所有节点,所以每次把栈内元素都访问完后,需要看看是否还剩下节点没走过,然后再从没有访问过的节点中挑一个重新开始深搜

Or bfs, if the node is already visited, then continue. Otherwise do a bfs starting with this node.

Union Find Set

Use a union find set, union the neighbors to the first neighbor node, return False if the current node has the same parent as the neighbor node.

Time complexity: o ( n log ⁡ n ) o(n\log n) o(nlogn)
Space complexity: o ( n ) o(n) o(n)

代码

DFS

class Solution:
    def isBipartite(self, graph: List[List[int]]) -> bool:
        visited_nodes = set()
        all_nodes = set([index for index in range(len(graph)) if graph[index]])
        while all_nodes - visited_nodes:
            stack = [(list(all_nodes - visited_nodes)[0], 'a')]
            cluster = {'a': [], 'b': []}
            while stack:
                node, node_cluster = stack.pop()
                if node not in visited_nodes:
                    visited_nodes.add(node)
                    cluster[node_cluster].append(node)
                    if node_cluster == 'a':
                        neighbor_cluster = 'b'
                    else:
                        neighbor_cluster = 'a'
                    for neighbor in graph[node]:
                        stack.append((neighbor, neighbor_cluster))
                else:
                    if node not in cluster[node_cluster]:
                        return False
        return True

BFS

class Solution:
    def isBipartite(self, graph: List[List[int]]) -> bool:
        visited = {}
        for i in range(len(graph)):
            if i in visited:
                continue
            queue = collections.deque([(i, 1)])
            while queue:
                node, color = queue.popleft()
                if node in visited and color != visited[node]:
                    return False
                elif node in visited:
                    continue
                visited[node] = color
                for neighbor_node in graph[node]:
                    queue.append((neighbor_node, -1 * color))
        return True

Union Find Set

class Solution:
    class UnionFindSet:
        def __init__(self, n: int) -> None:
            self.parent = [i for i in range(n)]
            self.height = [1] * n
        
        def find(self, x: int) -> int:
            if self.parent[x] != x:
                self.parent[x] = self.find(self.parent[x])
            return self.parent[x]

        def union(self, x: int, y: int) -> None:
            x, y = self.find(x), self.find(y)
            if x == y:
                return
            if self.height[x] > self.height[y]:
                self.parent[y] = x
            else:
                self.parent[y] = x
                if self.height[x] == self.height[y]:
                    self.height[x] += 1
        

    def isBipartite(self, graph: List[List[int]]) -> bool:
        n = len(graph)
        ufs = self.UnionFindSet(n)
        for i in range(n):
            for neighbor_i in range(len(graph[i])):
                if ufs.find(i) == ufs.find(graph[i][neighbor_i]):
                    return False
                ufs.union(graph[i][neighbor_i], graph[i][0])
        return True
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值