Leetcode785. Is Graph Bipartite?-二部图检测,递归-迭代-详细注释

Leetcode785. Is Graph Bipartite?

题目

题目链接

思路

  • 能将节点分成两个集合 U / V U/V U/V的图,就是二部图,若一个节点既能在 U U U,也能在 V V V中,那就不是二部图
  • DFS(Depth First Search,深度优先搜索)遍历着色,用两种颜色的画笔,交替地对遇到的节点着上不同颜色,未着色节点进行着色,并换成另一支画笔,若下一个节点已经着色,且颜色与手中的画笔不同(这个节点可以同时在 U U U V V V),则判断不是二部图
  • DFS可以用递归,也可以用迭代进行

复杂度

  • 时间复杂度 O ( n ) \mathcal{O}(n) O(n)
    所有的节点只访问一次,因此时间复杂度为 O ( n ) \mathcal{O}(n) O(n)

  • 空间复杂度 O ( n ) \mathcal{O}(n) O(n)
    空间复杂度上,递归的最坏情况,正好是迭代的最好情况,反过来不成立

  1. 递归,在一条路径上需要递归到最后一个节点才开始返回
    最坏情况,所有节点都在一条路径上, O ( n ) \mathcal{O}(n) O(n)
    最好情况,所有节点出入度都为1,即每个节点都只与一个节点相连,那么递归深度只有1,只保存了来路上的一个节点, O ( 1 ) \mathcal{O}(1) O(1)
  2. 迭代,访问一个节点时,将这个节点相连的所有节点加入stack中
    最坏情况, U U U中只有一个节点 u u u, V V V中的每个节点都与这个节点相连,访问 u u u时,将与它相连的所有节点,即 V V V中的所有节点都加入stack中, O ( n ) \mathcal{O}(n) O(n)
    最好情况,所有节点都在一条路径上,每次加入的子节点只有一个, O ( 1 ) \mathcal{O}(1) O(1)

代码

递归DFS遍历

class Solution:
    def isBipartite(self, graph: List[List[int]]) -> bool:
        colored = {}
        def dfs(i, color):
            if i in colored:  # 对已着色的判断是否矛盾
                return colored[i] == color
            # 对未着色的进行着色
            colored[i] = color
            # DFS访问相邻节点
            for nxt in graph[i]:
                if not dfs(nxt, color * (-1)):
                    return False
            # 顺利完成一条路径的dfs,没有产生冲突
            return True
        # 以图中的每个节点作为起点,进行dfs搜索
        for ii, g in enumerate(graph):
            # 当前点有相邻节点且未着色,则进入dfs
            if g and ii not in colored:
                # 任意一条路径返回False,则不是二部图
                if not dfs(ii, 1):
                    return False

        return True

借助于栈的DFS遍历-迭代

class Solution:
    def isBipartite(self, graph: List[List[int]]) -> bool:
        if graph == []:
            return True
        stack =  []
        color = {}
        visit = [False] * len(graph)
        
        # 借助于栈的DFS遍历
        while stack or (False in visit):
            if stack == []:
                # 将第一个未访问节点放入stack,并着色
                first_unvisited = visit.index(False)
                stack.append(first_unvisited)
                color[first_unvisited] = 1
            # 弹出,标记为已访问
            ind = stack.pop()
            if visit[ind] == False:
                visit[ind] = True
                # DFS访问相邻节点
                for n in graph[ind]:
                    if n in color:
                        # 对已着色的判断是否矛盾
                        if color[n] == color[ind]:
                            return False
                    else:  # 对未着色的进行着色
                        color[n] = -1 * color[ind]
                    # 将子节点加入stack,待下次循环访问
                    stack.append(n)
        # 循环正常结束,说明是二部图(没有出现着色矛盾)
        return True

漫谈

update:新开一篇在递归和迭代对比-栈结构

在前一篇Leetcode230. Kth Smallest Element in a BST中,对树进行遍历时,也有两种方式:递归和迭代
好像两个总是如影相随,细想一想:
递归的实现,也是借助于栈,只不过在程序中不可见,栈中保存的是当前节点的所有父节点,是访问到当前节点的路径,记忆的是来路,同级的兄弟节点是没有保存到栈中的
迭代中的栈程序中可见,直接拿出来用,用来保存当前节点的所有子节点,然后在访问的时候,由于当前节点已经弹出,所以,记忆的是去路
总结一下

栈的级别栈在程序中是否可见栈中保存的记忆是否保存同级节点
递归编译器父节点,来路
迭代用户程序子节点,去路
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值