Node With Highest Edge Score
MedPrompt
You are given a directed graph with n nodes labeled from 0 to n - 1, where each node has exactly one outgoing edge.
The graph is represented by a given 0-indexed integer array edges of length n, where edges[i] indicates that there is a directed edge from node i to node edges[i].
The edge score of a node i is defined as the sum of the labels of all the nodes that have an edge pointing to i.
Return the node with the highest edge score. If multiple nodes have the same edge score, return the node with the smallest index.
Example 1:
Input: edges = [1,0,0,0,0,7,7,5]
Output: 7
Explanation:
- The nodes 1, 2, 3 and 4 have an edge pointing to node 0. The edge score of node 0 is 1 + 2 + 3 + 4 = 10.
- The node 0 has an edge pointing to node 1. The edge score of node 1 is 0.
- The node 7 has an edge pointing to node 5. The edge score of node 5 is 7.
- The nodes 5 and 6 have an edge pointing to node 7. The edge score of node 7 is 5 + 6 = 11.
Node 7 has the highest edge score so return 7.Example 2:
Input: edges = [2,0,0,2]
Output: 0
Explanation:
- The nodes 1 and 2 have an edge pointing to node 0. The edge score of node 0 is 1 + 2 = 3.
- The nodes 0 and 3 have an edge pointing to node 2. The edge score of node 2 is 0 + 3 = 3.
Nodes 0 and 2 both have an edge score of 3. Since node 0 has a smaller index, we return 0.
Constraints:
n == edges.length2 <= n <= 1050 <= edges[i] < nedges[i] != i
Approaches
2 approaches with complexity analysis and trade-offs.
This approach directly translates the problem definition into code. It iterates through each node and, for each node, iterates through all edges again to find all incoming connections and calculate its score. This is straightforward but inefficient for the given constraints.
Algorithm
- Initialize
maxScoreto -1 andresultNodeto -1. - Iterate through each node
jfrom0ton-1. This nodejis the potential destination node whose score we want to calculate. - For each
j, initializecurrentScore = 0. - Start an inner loop, iterating through each node
ifrom0ton-1. This nodeiis the potential source node. - Inside the inner loop, check if there is an edge from
itoj(i.e., ifedges[i] == j). - If an edge exists, add the label of the source node,
i, tocurrentScore. - After the inner loop completes,
currentScoreholds the total edge score for nodej. - Compare
currentScorewithmaxScore. IfcurrentScoreis strictly greater, updatemaxScore = currentScoreandresultNode = j. - After the outer loop finishes,
resultNodewill hold the answer. The tie-breaking rule (smallest index) is handled automatically because we iteratejfrom0ton-1and only update for a strictly greater score.
Walkthrough
The brute-force method calculates the edge score for each node one by one. To find the score of a specific node j, we must scan the entire edges array to find all source nodes i that point to j.
We can use a nested loop structure. The outer loop iterates through every node j from 0 to n-1, treating it as the destination node. For each j, the inner loop iterates through every node i from 0 to n-1, treating it as a potential source node. If edges[i] is equal to j, it means there's an edge from i to j, so we add i to the score of j.
We maintain a variable maxScore to keep track of the highest score found so far and resultNode for the corresponding node's index. We update these whenever we find a node with a score greater than the current maxScore. The tie-breaking rule of returning the smallest index is naturally handled by iterating through the nodes in increasing order and only updating for strictly greater scores.
class Solution { public int edgeScore(int[] edges) { int n = edges.length; long maxScore = -1; int resultNode = -1; for (int j = 0; j < n; j++) { long currentScore = 0; for (int i = 0; i < n; i++) { if (edges[i] == j) { currentScore += i; } } if (currentScore > maxScore) { maxScore = currentScore; resultNode = j; } } return resultNode; }}Complexity
Time
O(n^2), where n is the number of nodes. For each of the n nodes, we iterate through the entire `edges` array of size n to find all incoming edges. This results in a nested loop structure, making it too slow for the problem's constraints.
Space
O(1). We only use a few variables to keep track of the maximum score and the corresponding node, requiring constant extra space regardless of the input size.
Trade-offs
Pros
Simple to understand and implement.
Uses constant extra space, O(1), making it very memory-efficient.
Cons
Highly inefficient due to the O(n^2) time complexity.
Will likely result in a 'Time Limit Exceeded' (TLE) error for inputs where n is large (e.g., n = 10^5).
A much more efficient approach is to calculate the scores for all nodes in a single pass. We can use an auxiliary array to store the scores. By iterating through the edges array just once, we can accumulate the scores for each destination node, avoiding the expensive nested loop.
Algorithm
- Get the number of nodes,
n, from the length of theedgesarray. - Create a
longarray namedscoresof sizenand initialize all its elements to0. This array will store the edge score for each node. - Iterate through the
edgesarray with an indexifrom0ton-1. - For each
i, the edge is fromitoedges[i]. Add the score contributionito the destination node:scores[edges[i]] += i. - After the loop, the
scoresarray contains the final edge score for every node. - Initialize
maxScore = -1andresultNode = -1. - Iterate through the
scoresarray from indexi = 0ton-1. - If
scores[i]is greater thanmaxScore, updatemaxScore = scores[i]andresultNode = i. - Return
resultNode.
Walkthrough
The inefficiency of the brute-force approach comes from repeatedly scanning the edges array. We can optimize this by changing our perspective. Instead of asking "what nodes point to me?" for each node, we can process each edge and say "I am contributing my label's value to my destination's score."
This leads to a two-pass linear time solution. In the first pass, we create an auxiliary array, scores, of size n to store the edge score for each node. A long array is necessary because the sum of labels can exceed the maximum value of a 32-bit integer. We iterate through the edges array from i = 0 to n-1. For each edge from i to edges[i], we add i to scores[edges[i]].
After this first pass, scores[j] will hold the total edge score for each node j. In the second pass, we simply iterate through the scores array to find the index that has the highest value. We maintain a maxScore and a resultNode and update them as we find higher scores. The tie-breaking rule is handled correctly by iterating from index 0 and only updating for strictly greater scores.
class Solution { public int edgeScore(int[] edges) { int n = edges.length; // Use long for scores to prevent overflow, as the sum of indices can be large. long[] scores = new long[n]; // First pass: calculate the score for each node. for (int i = 0; i < n; i++) { int destinationNode = edges[i]; scores[destinationNode] += i; } // Second pass: find the node with the highest score. long maxScore = -1; int resultNode = -1; for (int i = 0; i < n; i++) { if (scores[i] > maxScore) { maxScore = scores[i]; resultNode = i; } } return resultNode; }}Complexity
Time
O(n), where n is the number of nodes. We perform two separate, non-nested passes: one to populate the `scores` array and another to find the maximum score. The total time is O(n) + O(n) = O(n).
Space
O(n). We use an auxiliary array of size n to store the edge scores for each node. This is the dominant factor in space usage.
Trade-offs
Pros
Optimal time complexity of O(n), making it very fast and efficient.
Handles the given constraints with ease.
The logic is still straightforward and easy to follow.
Cons
Requires O(n) extra space for the scores array, which might be a concern for extremely memory-constrained environments, though it's acceptable for this problem.
Solutions
Solution
class Solution {public int edgeScore(int[] edges) { int n = edges.length; long[] cnt = new long[n]; for (int i = 0; i < n; ++i) { cnt[edges[i]] += i; } int ans = 0; for (int i = 0; i < n; ++i) { if (cnt[ans] < cnt[i]) { ans = i; } } return ans; }}class Solution {public: int edgeScore(vector<int> &edges) { int n = edges.size(); vector<long long> cnt(n); for (int i = 0; i < n; ++i) { cnt[edges[i]] += i; } int ans = 0; for (int i = 0; i < n; ++i) { if (cnt[ans] < cnt[i]) { ans = i; } } return ans; }};class Solution: def edgeScore(self, edges: List[int]) -> int: cnt = Counter() for i, v in enumerate(edges): cnt[v] += i ans = 0 for i in range(len(edges)): if cnt[ans] < cnt[i]: ans = i return ansVideo walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.