2374. Node With Highest Edge Score
Problem Description
You have a directed graph with n
nodes, where each node is labeled from 0
to n - 1
. Each node has exactly one outgoing edge (points to exactly one other node).
The graph is represented by an array edges
of length n
, where edges[i]
tells you that there is a directed edge from node i
to node edges[i]
.
The edge score of a node is calculated by adding up all the labels of nodes that point to it. For example, if nodes 2
, 3
, and 5
all point to node 7
, then the edge score of node 7
would be 2 + 3 + 5 = 10
.
Your task is to find the node with the highest edge score. If there's a tie (multiple nodes have the same highest score), return the node with the smallest index.
Example walkthrough:
-
If
edges = [1, 0, 0, 0, 2]
, then:- Node
0
points to node1
- Node
1
points to node0
- Node
2
points to node0
- Node
3
points to node0
- Node
4
points to node2
- Node
-
Edge scores would be:
- Node
0
: receives edges from nodes1
,2
,3
β score =1 + 2 + 3 = 6
- Node
1
: receives edge from node0
β score =0
- Node
2
: receives edge from node4
β score =4
- Nodes
3
and4
: no incoming edges β score =0
- Node
-
The answer would be node
0
with the highest score of6
.
Intuition
Since we need to calculate the edge score for each node (the sum of labels of all nodes pointing to it), we can think of this as a counting problem.
The key insight is that we don't need to build the entire graph structure or do any complex traversal. We simply need to track how much "score" each node accumulates from incoming edges.
As we iterate through the edges
array, for each node i
with its outgoing edge to node edges[i]
, we know that node i
contributes its label value (which is i
itself) to the edge score of node edges[i]
. So we can maintain a counter array where cnt[j]
keeps track of the total edge score for node j
.
The clever part is that we can update our answer on the fly as we process each edge. Whenever we update a node's edge score, we immediately check if this node should become our new answer. This way, we only need a single pass through the data.
For handling ties (when multiple nodes have the same score), we need to return the smallest index. So when comparing scores, if we find a node with a higher score, we update our answer. If we find a node with the same score as our current answer, we only update if this new node has a smaller index.
This approach is efficient because:
- We process each edge exactly once
- We don't need to store the graph structure
- We find the answer in a single traversal without needing a separate step to find the maximum
Learn more about Graph patterns.
Solution Approach
We implement the solution using a single traversal approach with an auxiliary array to track edge scores.
Data Structure:
- Create an array
cnt
of sizen
wherecnt[j]
stores the edge score of nodej
. Initialize all values to0
. - Maintain a variable
ans
to track the node with the highest edge score, initially set to0
.
Algorithm Steps:
-
Traverse the edges array: Use
enumerate
to iterate throughedges
, getting both indexi
(the source node) and valuej
(the destination node). -
Update edge scores: For each edge from node
i
to nodej
:- Add
i
tocnt[j]
:cnt[j] += i
- This accumulates the labels of all nodes pointing to
j
- Add
-
Update the answer: After updating
cnt[j]
, check if nodej
should become the new answer:- If
cnt[ans] < cnt[j]
: Nodej
has a higher score, so updateans = j
- If
cnt[ans] == cnt[j] and j < ans
: Nodej
has the same score but smaller index, so updateans = j
- If
-
Return the result: After processing all edges, return
ans
Example walkthrough with edges = [1, 0, 0, 0, 2]
:
- Initially:
cnt = [0, 0, 0, 0, 0]
,ans = 0
i=0, j=1
:cnt[1] += 0
βcnt = [0, 0, 0, 0, 0]
,ans = 0
i=1, j=0
:cnt[0] += 1
βcnt = [1, 0, 0, 0, 0]
,ans = 0
i=2, j=0
:cnt[0] += 2
βcnt = [3, 0, 0, 0, 0]
,ans = 0
i=3, j=0
:cnt[0] += 3
βcnt = [6, 0, 0, 0, 0]
,ans = 0
i=4, j=2
:cnt[2] += 4
βcnt = [6, 0, 4, 0, 0]
,ans = 0
- Return
0
(highest score is 6)
The time complexity is O(n)
as we traverse the edges array once, and space complexity is O(n)
for the cnt
array.
Ready to land your dream job?
Unlock your dream job with a 3-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a small example with edges = [2, 0, 1, 2]
.
This means:
- Node 0 β Node 2
- Node 1 β Node 0
- Node 2 β Node 1
- Node 3 β Node 2
Step-by-step execution:
Initialize: cnt = [0, 0, 0, 0]
, ans = 0
Processing edge from node 0 to node 2:
i = 0, j = 2
cnt[2] += 0
βcnt = [0, 0, 0, 0]
- Check if we should update
ans
:cnt[0] = 0
vscnt[2] = 0
(equal)- Since scores are equal but
ans = 0
<2
, keepans = 0
Processing edge from node 1 to node 0:
i = 1, j = 0
cnt[0] += 1
βcnt = [1, 0, 0, 0]
- Check if we should update
ans
:cnt[0] = 1
>cnt[0] = 1
(current ans is already 0)- Keep
ans = 0
Processing edge from node 2 to node 1:
i = 2, j = 1
cnt[1] += 2
βcnt = [1, 2, 0, 0]
- Check if we should update
ans
:cnt[0] = 1
<cnt[1] = 2
(node 1 has higher score)- Update
ans = 1
Processing edge from node 3 to node 2:
i = 3, j = 2
cnt[2] += 3
βcnt = [1, 2, 3, 0]
- Check if we should update
ans
:cnt[1] = 2
<cnt[2] = 3
(node 2 has higher score)- Update
ans = 2
Final result: Return 2
(node 2 has the highest edge score of 3)
The edge scores are:
- Node 0: score = 1 (from node 1)
- Node 1: score = 2 (from node 2)
- Node 2: score = 3 (from nodes 0 and 3: 0 + 3 = 3)
- Node 3: score = 0 (no incoming edges)
Solution Implementation
1class Solution:
2 def edgeScore(self, edges: List[int]) -> int:
3 # Initialize the node with the highest edge score (starts with node 0)
4 max_score_node = 0
5
6 # Create an array to store the edge score for each node
7 # edge_scores[i] represents the sum of all node indices that point to node i
8 edge_scores = [0] * len(edges)
9
10 # Iterate through each edge in the graph
11 # i is the source node index, target_node is the destination node
12 for source_node, target_node in enumerate(edges):
13 # Add the source node index to the edge score of the target node
14 edge_scores[target_node] += source_node
15
16 # Update the node with maximum edge score
17 # If current target node has higher score, or same score but smaller index
18 if (edge_scores[max_score_node] < edge_scores[target_node] or
19 (edge_scores[max_score_node] == edge_scores[target_node] and
20 target_node < max_score_node)):
21 max_score_node = target_node
22
23 # Return the node with the highest edge score
24 return max_score_node
25
1class Solution {
2 public int edgeScore(int[] edges) {
3 // Get the number of nodes
4 int numberOfNodes = edges.length;
5
6 // Array to store the edge score for each node
7 // Using long to prevent integer overflow when summing indices
8 long[] edgeScores = new long[numberOfNodes];
9
10 // Variable to track the node with the highest edge score
11 int nodeWithMaxScore = 0;
12
13 // Iterate through each node to calculate edge scores
14 for (int currentNode = 0; currentNode < numberOfNodes; ++currentNode) {
15 // Get the target node that current node points to
16 int targetNode = edges[currentNode];
17
18 // Add the current node's index to the target node's edge score
19 edgeScores[targetNode] += currentNode;
20
21 // Update the node with maximum score if:
22 // 1. Current target node has higher score than the current max, OR
23 // 2. Scores are equal but target node has smaller index (tiebreaker)
24 if (edgeScores[nodeWithMaxScore] < edgeScores[targetNode] ||
25 (edgeScores[nodeWithMaxScore] == edgeScores[targetNode] && targetNode < nodeWithMaxScore)) {
26 nodeWithMaxScore = targetNode;
27 }
28 }
29
30 // Return the node with the highest edge score
31 return nodeWithMaxScore;
32 }
33}
34
1class Solution {
2public:
3 int edgeScore(vector<int>& edges) {
4 // Get the number of nodes in the graph
5 int n = edges.size();
6
7 // Create an array to store the edge score for each node
8 // Using long long to prevent integer overflow when summing indices
9 vector<long long> edgeScores(n);
10
11 // Initialize the result with node 0
12 int maxScoreNode = 0;
13
14 // Iterate through each node to calculate edge scores
15 for (int currentNode = 0; currentNode < n; ++currentNode) {
16 // Get the target node that currentNode points to
17 int targetNode = edges[currentNode];
18
19 // Add the current node's index to the target node's edge score
20 edgeScores[targetNode] += currentNode;
21
22 // Update the node with maximum edge score
23 // If the new score is higher, or if scores are equal but target node has smaller index
24 if (edgeScores[maxScoreNode] < edgeScores[targetNode] ||
25 (edgeScores[maxScoreNode] == edgeScores[targetNode] && targetNode < maxScoreNode)) {
26 maxScoreNode = targetNode;
27 }
28 }
29
30 // Return the node with the highest edge score
31 return maxScoreNode;
32 }
33};
34
1/**
2 * Calculates the edge score by finding the node with the maximum sum of indices
3 * of nodes pointing to it. In case of a tie, returns the smaller node index.
4 *
5 * @param edges - Array where edges[i] represents an edge from node i to node edges[i]
6 * @returns The node index with the highest edge score
7 */
8function edgeScore(edges: number[]): number {
9 const nodeCount: number = edges.length;
10
11 // Array to store the sum of indices for each target node
12 const indexSumByNode: number[] = Array(nodeCount).fill(0);
13
14 // Track the node with the maximum score
15 let maxScoreNode: number = 0;
16
17 // Iterate through each edge and calculate scores
18 for (let sourceNode = 0; sourceNode < nodeCount; ++sourceNode) {
19 const targetNode: number = edges[sourceNode];
20
21 // Add the source node index to the target node's score
22 indexSumByNode[targetNode] += sourceNode;
23
24 // Update the answer if we found a better node:
25 // 1. Higher score, or
26 // 2. Same score but smaller node index
27 if (indexSumByNode[maxScoreNode] < indexSumByNode[targetNode] ||
28 (indexSumByNode[maxScoreNode] === indexSumByNode[targetNode] && targetNode < maxScoreNode)) {
29 maxScoreNode = targetNode;
30 }
31 }
32
33 return maxScoreNode;
34}
35
Time and Space Complexity
The time complexity is O(n)
, where n
is the length of the array edges
. The algorithm iterates through the edges
array exactly once using enumerate()
, performing constant-time operations for each element: accessing and updating the cnt
array, comparing values, and potentially updating the ans
variable.
The space complexity is O(n)
, where n
is the length of the array edges
. This is due to the creation of the cnt
array with size equal to len(edges)
, which stores the accumulated scores for each node. The remaining variables (ans
, i
, j
) use constant space.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Incorrect Tie-Breaking Logic
A common mistake is implementing the tie-breaking condition incorrectly when multiple nodes have the same highest edge score.
Incorrect Implementation:
# Wrong: This updates even when target_node has a larger index if edge_scores[max_score_node] <= edge_scores[target_node]: max_score_node = target_node
Why it's wrong: Using <=
without checking the index means that when scores are equal, we always update to the latest node we encounter, not necessarily the one with the smallest index.
Correct Implementation:
if (edge_scores[max_score_node] < edge_scores[target_node] or (edge_scores[max_score_node] == edge_scores[target_node] and target_node < max_score_node)): max_score_node = target_node
2. Integer Overflow in Other Languages
While Python handles large integers automatically, in languages like Java or C++, the sum of node indices could overflow for large graphs.
Problem Example: For a graph with n=100,000 nodes where many nodes point to a single node, the edge score could exceed the range of a 32-bit integer.
Solution: Use appropriate data types:
- Java: Use
long[]
instead ofint[]
for edge scores - C++: Use
long long
instead ofint
// Java example long[] edgeScores = new long[edges.length]; // Use long to prevent overflow
3. Forgetting to Initialize the Answer Variable
Some implementations might forget to properly initialize the answer variable or start with an invalid node index.
Incorrect:
max_score_node = -1 # Wrong: -1 is not a valid node # or forgetting to initialize at all
Correct:
max_score_node = 0 # Start with node 0 as the initial answer
This ensures that even if all nodes have an edge score of 0, we still return a valid node (node 0).
Is the following code DFS or BFS?
void search(Node root) { if (!root) return; visit(root); root.visited = true; for (Node node in root.adjacent) { if (!node.visited) { search(node); } } }
Recommended Readings
https assets algo monster cover_photos graph svg Graph Fundamentals Tree with 0 cycle At this point you should be pretty familiar with trees A tree is a special kind of graph a connected acyclic cycle less graph A graph may contain cycle s and nodes could be disconnected A tree
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Donβt Miss This!