当前位置: 首页 > news >正文

每日一题-力扣-2360. 图中的最长环 0329

寻找有向图中的最长环:两种解法的深度解析

在算法问题中,寻找有向图中的最长环是一个经典问题。今天,我将分享两种不同的解法:时间戳法拓扑排序法。这两种方法各有特点,适用于不同场景。
在这里插入图片描述

问题描述

给定一个包含 n 个节点的有向图,节点编号为 0n-1,每个节点最多有一个出边。图的结构由数组 edges 表示,其中 edges[i] 表示节点 i 指向的节点,若 edges[i] = -1 则表示无出边。要求找出图中最长的环,若无环则返回 -1

示例1

输入:edges = [3,3,4,2,3]
输出:3
解释:最长环为 2 -> 4 -> 3 -> 2,长度为 3。

示例2

输入:edges = [2,-1,3,1]
输出:-1
解释:图中无环。

方法一:时间戳法

核心思想

时间戳法利用访问顺序的时间戳来检测环。通过记录每个节点的访问时间,当再次访问到某个节点时,若该节点在当前路径中,则说明形成了一个环。环的长度可以通过时间差计算。

实现步骤

  1. 初始化:使用全局标记数组 visited 避免重复处理节点。
  2. 遍历节点:对每个未访问的节点,沿出边遍历,记录路径中的节点及其访问时间。
  3. 检测环:若遇到当前路径中的已访问节点,计算环长度;否则终止遍历。
  4. 更新结果:维护最大环长度。

代码实现

class Solution:
    def longestCycle(self, edges: List[int]) -> int:
        n = len(edges)
        visited = [False] * n  # 全局访问标记
        max_cycle = -1
        
        for i in range(n):
            if not visited[i]:
                time_map = {}  # 当前路径的时间戳记录
                current_node = i
                time = 0
                while True:
                    if current_node == -1:  # 无出边,终止
                        break
                    if current_node in time_map:  # 发现环
                        cycle_length = time - time_map[current_node]
                        max_cycle = max(max_cycle, cycle_length)
                        break
                    if visited[current_node]:  # 已访问过,但不在当前路径中
                        break
                    time_map[current_node] = time  # 记录当前节点的时间
                    visited[current_node] = True     # 标记为已访问
                    current_node = edges[current_node]  # 移动到下一个节点
                    time += 1
        return max_cycle if max_cycle != -1 else -1

复杂度分析

  • 时间复杂度:O(n),每个节点仅被访问一次。
  • 空间复杂度:O(n),存储访问标记和时间戳。

适用场景

时间戳法适用于单出边图的最长环问题,能够高效地检测环并计算其长度。

方法二:拓扑排序法

核心思想

拓扑排序法通过消除图中的非环部分,剩下的节点必然构成环。具体步骤如下:

  1. 计算入度:统计每个节点的入度。
  2. 拓扑排序:使用队列处理所有入度为0的节点,逐步消除非环节点。
  3. 遍历剩余节点:未被访问的节点构成环,计算每个环的长度。

实现步骤

  1. 计算入度:遍历每个节点,统计每个节点的入度。
  2. 拓扑排序:将入度为0的节点加入队列,逐步减少其他节点的入度。
  3. 遍历剩余节点:未被访问的节点属于环,计算每个环的长度。

代码实现

from typing import List
from collections import deque

class Solution:
    def longestCycle(self, edges: List[int]) -> int:
        n = len(edges)
        indegree = [0] * n  # 记录每个节点的入度
        
        # 计算每个节点的入度
        for u in range(n):
            v = edges[u]
            if v != -1:
                indegree[v] += 1
        
        visited = [False] * n  # 标记是否被访问过
        q = deque()
        
        # 初始化队列,将入度为0的节点加入队列
        for u in range(n):
            if indegree[u] == 0:
                q.append(u)
        
        # 进行拓扑排序,消除非环节点
        while q:
            u = q.popleft()
            visited[u] = True
            v = edges[u]
            if v != -1:
                indegree[v] -= 1
                if indegree[v] == 0:
                    q.append(v)
        
        max_length = -1  # 记录最长环的长度
        
        # 遍历剩余未被访问的节点,计算每个环的长度
        for u in range(n):
            if not visited[u]:
                count = 0
                current = u
                while True:
                    count += 1
                    visited[current] = True
                    current = edges[current]
                    if current == u:
                        break
                if count > max_length:
                    max_length = count
        
        return max_length if max_length != 0 else -1

复杂度分析

  • 时间复杂度:O(n),每个节点仅被访问一次。
  • 空间复杂度:O(n),存储入度和访问标记。

适用场景

拓扑排序法适用于单出边图的最长环问题,能够高效地分离出环部分并计算其长度。

两种方法的对比

方法优点缺点
时间戳法实现简单,直接检测环需要额外的时间戳记录
拓扑排序法空间效率高,分离非环节点需要两次遍历(拓扑排序和环检测)

选择建议

  • 时间戳法:适用于需要直接检测环的场景,代码实现较为直观。
  • 拓扑排序法:适用于需要分离非环节点的场景,适合大规模图的处理。

总结

两种寻找有向图中最长环的方法:时间戳法和拓扑排序法。两种方法各有优缺点,但都能高效解决问题。时间戳法通过记录访问时间直接检测环,而拓扑排序法则通过消除非环节点来间接找到环。

http://www.dtcms.com/a/98090.html

相关文章:

  • Java 大视界 -- 基于 Java 的大数据分布式计算在基因测序数据分析中的性能优化(161)
  • 力扣刷题1049. 最后一块石头的重量 II
  • Swiss Dock:免费的在线分子对接工具(支持两种对接算法切换)
  • 信号与系统(郑君里)第一章-绪论 1-23 课后习题解答
  • 基于Java与Go的下一代DDoS防御体系构建实战
  • 图像数据增强教程:为目标检测任务准备数据
  • 基于RFID技术建筑物资材料智能管理解决方案
  • 迭代器与可迭代对象
  • 《汽车理论》第三章作业
  • 电脑ip地址每次开机会换吗?全面解析
  • 蓝桥杯 之 图论基础+并查集
  • 什么是openAPI
  • IDEA如何设置以新窗口打开新项目
  • 基于RK3588的YOLO多线程推理多级硬件加速引擎框架设计(项目总览和加速效果)
  • css属性列举
  • C++ 仿函数详解:让对象像函数一样调用
  • 15届蓝桥JavaB组 前6道题解
  • React(七):Redux
  • 网络安全 - SQL Injection
  • 从24GHz到71GHz:Sivers半导体的广泛频率范围5G毫米波产品解析
  • 全流程剖析需求开发:打造极致贴合用户的产品
  • 如何用Appuploader 快速一键发行苹果IOS开发者账户的开发者证书-发行cer证书以及转换.p12证书-优雅草卓伊凡
  • 洛谷题单1-P5704 【深基2.例6】字母转换-python-流程图重构
  • 【MyBatis】MyBatis 操作数据库(入门)
  • 庙算兵棋推演AI开发初探(6-神经网络开发)
  • prompt_status:5: command not found: wc解决办法
  • 解锁无痕采集的终极奥秘
  • 蓝桥杯省模拟赛 质因数之和
  • C++_STL之vector篇
  • 深入解析 HotSpot 的经典垃圾收集器