力扣热题-有向图中最大颜色值

本文介绍了力扣1857题"有向图中最大颜色值"的解法。题目要求在给定有向图中找出路径中出现次数最多的颜色值,若图中存在环则返回-1。主要解法采用拓扑排序检测环,并结合动态规划维护各节点26种颜色的最大出现次数。时间复杂度为O(n+m26),空间复杂度为O(n26+m)。Java和C++实现均通过拓扑排序处理节点,动态规划更新颜色状态,最终返回最大颜色值或检测到环时返回-1。该解法有效结合了图论算法和动态规划技巧来解决问题。

目录

[题目链接:1857. 有向图中最大颜色值 - 力扣(LeetCode)](#题目链接:1857. 有向图中最大颜色值 - 力扣(LeetCode))

题目描述

解法一:图论中的环检测+动态规划的状态传递

Java写法:

C++写法:

运行时间

时间复杂度和空间复杂度

总结


题目链接:1857. 有向图中最大颜色值 - 力扣(LeetCode)

注:下述题目描述和示例均来自力扣

题目描述

给你一个 有向图 ,它含有 n 个节点和 m 条边。节点编号从 0n - 1

给你一个字符串 colors ,其中 colors[i] 是小写英文字母,表示图中第 i 个节点的 颜色 (下标从 0 开始)。同时给你一个二维数组 edges ,其中 edges[j] = [aj, bj] 表示从节点 aj 到节点 bj 有一条 有向边

图中一条有效 路径 是一个点序列 x1 -> x2 -> x3 -> ... -> xk ,对于所有 1 <= i < k ,从 xixi+1 在图中有一条有向边。路径的 颜色值 是路径中 出现次数最多 颜色的节点数目。

请你返回给定图中有效路径里面的 最大颜色值 如果图中含有环,请返回 -1

示例 1:

复制代码
输入:colors = "abaca", edges = [[0,1],[0,2],[2,3],[3,4]]
输出:3
解释:路径 0 -> 2 -> 3 -> 4 含有 3 个颜色为 "a" 的节点(上图中的红色节点)。

示例 2:

复制代码
输入:colors = "a", edges = [[0,0]]
输出:-1
解释:从 0 到 0 有一个环。

提示:

  • n == colors.length
  • m == edges.length
  • 1 <= n <=
  • 0 <= m <=
  • colors 只含有小写英文字母。
  • 0 <= aj, bj < n

解法一:图论中的环检测+动态规划的状态传递

这道题的解题思路需要结合图论中的环检测和动态规划的状态传递。首先得明确题目要找的是所有有效路径中颜色出现次数最多的那个值,同时还要处理图中可能存在环的情况。这里的关键在于如何高效地遍历可能的路径并统计颜色出现次数,同时避免陷入环的死循环。

整个思路可以分成两部分来看。第一部分是判断图中是否存在环,这可以通过拓扑排序来解决。拓扑排序的原理是按节点入度逐步处理,如果最终处理过的节点数量不等于总节点数,说明存在环。这一步不仅是环检测的基础,也为后续的动态规划提供了处理顺序------因为拓扑排序保证了在处理每个节点时,所有可能到达它的前驱节点都已经被处理过。

第二部分是动态规划维护颜色状态。每个节点需要记录以它为终点的所有路径中,各个颜色出现的最大次数。比如当前节点颜色是红色,那么它的红色计数需要在前驱节点红色计数的基础上加1,其他颜色则继承前驱中的最大值。这种设计巧妙地利用了路径的叠加性质------不同前驱带来的颜色分布会被动态比较,保留最大值继续向后传递。

具体来说,当处理拓扑排序中的某个节点时,会先用它的颜色更新自己的计数,然后将这个状态传递给所有后继节点。每个后继节点会综合所有前驱传递来的状态,取各颜色的最大值作为自己的基准状态。这样层层传递下去,最终每个节点记录的颜色最大值实际上代表了以它为终点的所有路径中颜色分布的极限情况。

整个过程像流水线上的装配,每个节点在拓扑顺序中被"激活"时,已经收集了所有可能影响它的路径信息。最后只需要遍历所有节点的颜色记录,就能找到全局最大值。如果中途发现环,直接返回-1,这相当于在流水线检测到故障时立即停机报错。

Java写法:

java 复制代码
import java.util.*;

class Solution {
    public int largestPathValue(String colors, int[][] edges) {
        int n = colors.length();
        List<List<Integer>> graph = new ArrayList<>();
        int[] indeg = new int[n];
        int[][] dp = new int[n][26]; // dp[i][c] 表示以节点i结尾的路径中颜色c的最大出现次数
        
        // 初始化邻接表
        for (int i = 0; i < n; i++) graph.add(new ArrayList<>());
        for (int[] e : edges) {
            graph.get(e[0]).add(e[1]);
            indeg[e[1]]++;
        }
        
        // 拓扑排序队列初始化
        Queue<Integer> q = new LinkedList<>();
        for (int i = 0; i < n; i++) {
            if (indeg[i] == 0) {
                q.offer(i);
                dp[i][colors.charAt(i) - 'a'] = 1; // 自身颜色初始化为1
            }
        }
        
        int processed = 0, max = 0;
        while (!q.isEmpty()) {
            int u = q.poll();
            processed++;
            int curMax = Arrays.stream(dp[u]).max().getAsInt();
            max = Math.max(max, curMax);
            
            for (int v : graph.get(u)) {
                for (int c = 0; c < 26; c++) {
                    // 继承u的颜色计数,若当前节点颜色为c则+1
                    int newVal = dp[u][c] + (colors.charAt(v) - 'a' == c ? 1 : 0);
                    dp[v][c] = Math.max(dp[v][c], newVal);
                }
                indeg[v]--;
                if (indeg[v] == 0) q.offer(v);
            }
        }
        
        return processed == n ? max : -1; // 检测环
    }
}

C++写法:

cpp 复制代码
#include <vector>
#include <queue>
#include <algorithm>
using namespace std;

class Solution {
public:
    int largestPathValue(string colors, vector<vector<int>>& edges) {
        int n = colors.size();
        vector<vector<int>> graph(n);
        vector<int> indeg(n, 0);
        vector<vector<int>> dp(n, vector<int>(26, 0)); // dp[i][c] 记录颜色c的最大出现次数
        
        // 构建邻接表和入度数组
        for (auto& e : edges) {
            graph[e[0]].push_back(e[1]);
            indeg[e[1]]++;
        }
        
        queue<int> q;
        for (int i = 0; i < n; i++) {
            if (indeg[i] == 0) {
                q.push(i);
                dp[i][colors[i] - 'a'] = 1; // 初始化自身颜色
            }
        }
        
        int processed = 0, max_val = 0;
        while (!q.empty()) {
            int u = q.front();
            q.pop();
            processed++;
            int cur_max = *max_element(dp[u].begin(), dp[u].end());
            max_val = max(max_val, cur_max);
            
            for (int v : graph[u]) {
                for (int c = 0; c < 26; c++) {
                    // 更新后继节点的颜色计数
                    int new_val = dp[u][c] + (colors[v] - 'a' == c ? 1 : 0);
                    dp[v][c] = max(dp[v][c], new_val);
                }
                indeg[v]--;
                if (indeg[v] == 0) q.push(v);
            }
        }
        
        return processed == n ? max_val : -1; // 环检测
    }
};

运行时间

时间复杂度和空间复杂度

复杂度主要分两部分来看。拓扑排序处理环检测和节点遍历,这部分的时间是线性的,和节点数加边数成正比。因为每个节点和边各处理一次,所以时间复杂度是O(n + m)。动态规划维护颜色状态的部分,每条边在传递时要处理26种颜色,相当于每条边要乘上颜色种类的常数,所以整体的时间复杂度是O(n + m*26)。对于一般的问题规模,这个复杂度是可以接受的,特别是当边数不是特别大的时候。

空间上,最大的开销来自记录每个节点的26种颜色出现次数的二维数组,这部分占用了O(n26)的空间。邻接表存储图结构需要O(n + m)的空间,入度数组和队列占用的空间相对较小。综合起来,空间复杂度是O(n26 + m)。这样的设计在空间利用上是比较紧凑的,没有浪费额外的内存,主要开销都花在了必要的数据结构上。



总结

本文介绍了力扣1857题"有向图中最大颜色值"的解法。题目要求在给定有向图中找出路径中出现次数最多的颜色值,若图中存在环则返回-1。主要解法采用拓扑排序检测环,并结合动态规划维护各节点26种颜色的最大出现次数。时间复杂度为O(n+m26),空间复杂度为O(n26+m)。Java和C++实现均通过拓扑排序处理节点,动态规划更新颜色状态,最终返回最大颜色值或检测到环时返回-1。该解法有效结合了图论算法和动态规划技巧来解决问题。

相关推荐
是乐谷1 分钟前
把数据库做得能扩展:Aurora DSQL 的故事
数据库·人工智能·python·程序人生·面试·职场和发展
独行soc26 分钟前
2025年渗透测试面试题总结-匿名[校招]安全服务工程师(题目+回答)
linux·python·安全·web安全·面试·职场和发展·渗透测试
JK0x071 小时前
代码随想录算法训练营 Day58 图论Ⅷ 拓扑排序 Dijkstra
android·算法·图论
oioihoii2 小时前
C++23 <spanstream>:基于 std::span 的高效字符串流处理
c++·算法·c++23
白熊1882 小时前
【机器学习基础】机器学习入门核心算法:朴素贝叶斯(Naive Bayes)
人工智能·算法·机器学习
Owen_Q2 小时前
AtCoder Beginner Contest 407
开发语言·c++·算法
客卿1233 小时前
力扣100题---字母异位词分组
算法·leetcode·职场和发展
JK0x073 小时前
代码随想录算法训练营 Day56 图论Ⅶ 最小生成树算法 Prim Kruskal
算法·图论
javthon实验室3 小时前
【Prompt Engineering】摸索出的一些小套路
算法·prompt
山楂树の3 小时前
Three.js 直线拐角自动圆角化(圆弧转弯)
算法·3d·webgl