Facebook Pixel

2508. Add Edges to Make Degrees of All Nodes Even

HardGraphHash Table
Leetcode Link

Problem Description

You are given an undirected graph with n nodes numbered from 1 to n. The graph is defined by a 2D array edges where edges[i] = [ai, bi] represents an edge between nodes ai and bi. The graph may be disconnected.

Your task is to determine if you can add at most two additional edges (possibly none) to make every node in the graph have an even degree. The degree of a node is the number of edges connected to it.

Rules for adding edges:

  • No repeated edges allowed (no duplicate edges)
  • No self-loops allowed (a node cannot connect to itself)

Return true if it's possible to make all nodes have even degrees by adding at most 2 edges, otherwise return false.

Example 1: Given n = 5 and edges connecting nodes as shown, you can add one edge to make all nodes have even degrees, so return true.

Example 2: Given n = 4 with edges [[1,2],[3,4]], you can add two edges (like connecting 1-3 and 2-4) to make all nodes have even degrees, so return true.

Example 3: Given n = 4 with edges [[1,2],[1,3],[1,4]], node 1 has degree 3 (odd) and nodes 2, 3, 4 each have degree 1 (odd). It's impossible to make all nodes have even degrees by adding at most 2 edges, so return false.

The key insight is that adding an edge between two nodes increases the degree of both nodes by 1. Since you can add at most 2 edges, you can increase degrees for at most 4 nodes. This means if there are more than 4 nodes with odd degrees, it's impossible to fix them all.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The key observation is that adding an edge between two nodes increases both their degrees by 1. Since we want all nodes to have even degrees, we need to identify which nodes currently have odd degrees and figure out how to make them even.

Let's think about what happens when we add edges:

  • Adding 1 edge affects exactly 2 nodes (increases each by 1)
  • Adding 2 edges affects at most 4 nodes

This immediately tells us that if we have more than 4 nodes with odd degrees, it's impossible to fix them all with at most 2 edges.

Now let's analyze the different cases:

Case 1: 0 nodes with odd degree All nodes already have even degrees, so we don't need to add any edges. Return true.

Case 2: 2 nodes with odd degree We have nodes a and b with odd degrees. If we add an edge between them, both become even. But what if they're already connected? Then we need to find a "helper" node c that isn't connected to either a or b. We can add edges a-c and b-c, which makes all three nodes change parity (odd→even for a and b, and c stays even if it was even or becomes even if it was odd).

Case 3: 4 nodes with odd degree We have nodes a, b, c, d with odd degrees. We need to pair them up somehow using 2 edges. There are three possible pairings:

  • Connect a-b and c-d
  • Connect a-c and b-d
  • Connect a-d and b-c

We check if any of these pairings is valid (i.e., the edges don't already exist).

Case 4: Any other number of odd-degree nodes For 1 or 3 nodes with odd degree, it's impossible because each edge changes exactly 2 nodes' parity. For more than 4 nodes with odd degree, we can't fix all of them with just 2 edges.

The solution naturally follows from counting odd-degree nodes and handling each case appropriately.

Learn more about Graph patterns.

Solution Approach

The implementation follows our case analysis strategy:

Step 1: Build the graph representation We use a defaultdict(set) to store the adjacency list. For each edge [a, b], we add b to g[a] and a to g[b]. Using sets helps us quickly check if an edge exists between two nodes.

g = defaultdict(set)
for a, b in edges:
    g[a].add(b)
    g[b].add(a)

Step 2: Find all nodes with odd degrees We iterate through the graph and collect nodes whose degree (size of their adjacency set) is odd:

vs = [i for i, v in g.items() if len(v) & 1]

The expression len(v) & 1 checks if the degree is odd (bitwise AND with 1).

Step 3: Handle different cases based on the count of odd-degree nodes

Case 0 nodes: All nodes have even degrees already.

if len(vs) == 0:
    return True

Case 2 nodes: Try to connect them directly, or find a common third node.

if len(vs) == 2:
    a, b = vs
    if a not in g[b]:  # Can directly connect a and b
        return True
    # Try to find a node c that can connect to both a and b
    return any(a not in g[c] and c not in g[b] for c in range(1, n + 1))

Case 4 nodes: Try all three possible pairings.

if len(vs) == 4:
    a, b, c, d = vs
    if a not in g[b] and c not in g[d]:  # Pair (a,b) and (c,d)
        return True
    if a not in g[c] and b not in g[d]:  # Pair (a,c) and (b,d)
        return True
    if a not in g[d] and b not in g[c]:  # Pair (a,d) and (b,c)
        return True
    return False

Any other case: Return false as it's impossible to make all degrees even with at most 2 edges.

return False

The time complexity is O(E + N) where E is the number of edges (for building the graph) and N is the number of nodes (for checking potential connections in the worst case). The space complexity is O(E) for storing the graph.

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through a concrete example to illustrate the solution approach.

Example: n = 6, edges = [[1,2], [2,3], [3,4], [4,5]]

Step 1: Build the graph

Node 1: connected to {2}      β†’ degree = 1 (odd)
Node 2: connected to {1, 3}   β†’ degree = 2 (even)
Node 3: connected to {2, 4}   β†’ degree = 2 (even)
Node 4: connected to {3, 5}   β†’ degree = 2 (even)
Node 5: connected to {4}      β†’ degree = 1 (odd)
Node 6: connected to {}       β†’ degree = 0 (even)

Step 2: Find odd-degree nodes Nodes with odd degrees: [1, 5] Count = 2

Step 3: Apply Case 2 logic Since we have exactly 2 nodes with odd degrees, we check:

  • Can we directly connect nodes 1 and 5?
    • Check if edge (1,5) already exists: No, it doesn't exist
    • We can add edge (1,5)!

After adding edge (1,5):

  • Node 1: degree becomes 2 (even) βœ“
  • Node 5: degree becomes 2 (even) βœ“
  • All other nodes remain even βœ“

Result: Return true - we made all degrees even by adding just 1 edge.


Another Example: n = 4, edges = [[1,2], [2,3], [3,1]] (a triangle)

Step 1: Build the graph

Node 1: connected to {2, 3}   β†’ degree = 2 (even)
Node 2: connected to {1, 3}   β†’ degree = 2 (even)
Node 3: connected to {1, 2}   β†’ degree = 2 (even)
Node 4: connected to {}       β†’ degree = 0 (even)

Step 2: Find odd-degree nodes Nodes with odd degrees: [] Count = 0

Step 3: Apply Case 0 logic All nodes already have even degrees!

Result: Return true - no edges need to be added.


Impossible Example: n = 4, edges = [[1,2], [1,3], [1,4]] (star graph)

Step 1: Build the graph

Node 1: connected to {2, 3, 4} β†’ degree = 3 (odd)
Node 2: connected to {1}       β†’ degree = 1 (odd)
Node 3: connected to {1}       β†’ degree = 1 (odd)
Node 4: connected to {1}       β†’ degree = 1 (odd)

Step 2: Find odd-degree nodes Nodes with odd degrees: [1, 2, 3, 4] Count = 4

Step 3: Apply Case 4 logic We need to try all possible pairings:

  1. Pair (1,2) and (3,4):
    • Edge (1,2) already exists βœ—
    • Cannot use this pairing
  2. Pair (1,3) and (2,4):
    • Edge (1,3) already exists βœ—
    • Cannot use this pairing
  3. Pair (1,4) and (2,3):
    • Edge (1,4) already exists βœ—
    • Cannot use this pairing

All possible pairings fail because node 1 is already connected to all other nodes.

Result: Return false - impossible to make all degrees even with at most 2 edges.

Solution Implementation

1class Solution:
2    def isPossible(self, n: int, edges: List[List[int]]) -> bool:
3        # Build adjacency list representation of the graph
4        adjacency_list = defaultdict(set)
5        for node_a, node_b in edges:
6            adjacency_list[node_a].add(node_b)
7            adjacency_list[node_b].add(node_a)
8      
9        # Find all vertices with odd degree
10        odd_degree_vertices = [vertex for vertex, neighbors in adjacency_list.items() 
11                               if len(neighbors) % 2 == 1]
12      
13        # If all vertices have even degree, graph is already Eulerian
14        if len(odd_degree_vertices) == 0:
15            return True
16      
17        # If exactly 2 vertices have odd degree
18        if len(odd_degree_vertices) == 2:
19            vertex_a, vertex_b = odd_degree_vertices
20          
21            # Check if we can directly connect these two vertices
22            if vertex_a not in adjacency_list[vertex_b]:
23                return True
24          
25            # Try to find an intermediate vertex to connect both odd-degree vertices
26            for intermediate_vertex in range(1, n + 1):
27                if (vertex_a not in adjacency_list[intermediate_vertex] and 
28                    intermediate_vertex not in adjacency_list[vertex_b]):
29                    return True
30            return False
31      
32        # If exactly 4 vertices have odd degree
33        if len(odd_degree_vertices) == 4:
34            vertex_a, vertex_b, vertex_c, vertex_d = odd_degree_vertices
35          
36            # Try all three possible pairings to connect the 4 vertices
37            # Pairing 1: (a,b) and (c,d)
38            if (vertex_a not in adjacency_list[vertex_b] and 
39                vertex_c not in adjacency_list[vertex_d]):
40                return True
41          
42            # Pairing 2: (a,c) and (b,d)
43            if (vertex_a not in adjacency_list[vertex_c] and 
44                vertex_b not in adjacency_list[vertex_d]):
45                return True
46          
47            # Pairing 3: (a,d) and (b,c)
48            if (vertex_a not in adjacency_list[vertex_d] and 
49                vertex_b not in adjacency_list[vertex_c]):
50                return True
51          
52            return False
53      
54        # More than 4 vertices with odd degree cannot be fixed with at most 2 edges
55        return False
56
1class Solution {
2    public boolean isPossible(int n, List<List<Integer>> edges) {
3        // Create adjacency list representation of the graph
4        // Using Set to avoid duplicate edges and for O(1) lookup
5        Set<Integer>[] graph = new Set[n + 1];
6        Arrays.setAll(graph, index -> new HashSet<>());
7      
8        // Build the undirected graph from edge list
9        for (List<Integer> edge : edges) {
10            int nodeA = edge.get(0);
11            int nodeB = edge.get(1);
12            graph[nodeA].add(nodeB);
13            graph[nodeB].add(nodeA);
14        }
15      
16        // Find all vertices with odd degree
17        // For Eulerian path to exist, we need all vertices to have even degree
18        List<Integer> oddDegreeVertices = new ArrayList<>();
19        for (int vertex = 1; vertex <= n; ++vertex) {
20            if (graph[vertex].size() % 2 == 1) {
21                oddDegreeVertices.add(vertex);
22            }
23        }
24      
25        // Case 1: No vertices with odd degree - already Eulerian
26        if (oddDegreeVertices.size() == 0) {
27            return true;
28        }
29      
30        // Case 2: Exactly 2 vertices with odd degree
31        if (oddDegreeVertices.size() == 2) {
32            int vertexA = oddDegreeVertices.get(0);
33            int vertexB = oddDegreeVertices.get(1);
34          
35            // Can directly connect the two odd-degree vertices
36            if (!graph[vertexA].contains(vertexB)) {
37                return true;
38            }
39          
40            // Try to find an intermediate vertex to connect both odd-degree vertices
41            for (int intermediateVertex = 1; intermediateVertex <= n; ++intermediateVertex) {
42                if (intermediateVertex != vertexA && 
43                    intermediateVertex != vertexB && 
44                    !graph[vertexA].contains(intermediateVertex) && 
45                    !graph[intermediateVertex].contains(vertexB)) {
46                    return true;
47                }
48            }
49            return false;
50        }
51      
52        // Case 3: Exactly 4 vertices with odd degree
53        if (oddDegreeVertices.size() == 4) {
54            int vertexA = oddDegreeVertices.get(0);
55            int vertexB = oddDegreeVertices.get(1);
56            int vertexC = oddDegreeVertices.get(2);
57            int vertexD = oddDegreeVertices.get(3);
58          
59            // Try all possible pairings to make all degrees even
60            // Pairing 1: (A-B) and (C-D)
61            if (!graph[vertexA].contains(vertexB) && !graph[vertexC].contains(vertexD)) {
62                return true;
63            }
64            // Pairing 2: (A-C) and (B-D)
65            if (!graph[vertexA].contains(vertexC) && !graph[vertexB].contains(vertexD)) {
66                return true;
67            }
68            // Pairing 3: (A-D) and (B-C)
69            if (!graph[vertexA].contains(vertexD) && !graph[vertexB].contains(vertexC)) {
70                return true;
71            }
72            return false;
73        }
74      
75        // More than 4 vertices with odd degree - impossible to fix with at most 2 edges
76        return false;
77    }
78}
79
1class Solution {
2public:
3    bool isPossible(int n, vector<vector<int>>& edges) {
4        // Build adjacency list using unordered_set for O(1) lookup
5        // Graph is 1-indexed (vertices from 1 to n)
6        vector<unordered_set<int>> adjacencyList(n + 1);
7      
8        // Populate the adjacency list with undirected edges
9        for (auto& edge : edges) {
10            int vertex1 = edge[0];
11            int vertex2 = edge[1];
12            adjacencyList[vertex1].insert(vertex2);
13            adjacencyList[vertex2].insert(vertex1);
14        }
15      
16        // Find all vertices with odd degree
17        vector<int> oddDegreeVertices;
18        for (int vertex = 1; vertex <= n; ++vertex) {
19            if (adjacencyList[vertex].size() % 2 == 1) {
20                oddDegreeVertices.emplace_back(vertex);
21            }
22        }
23      
24        // If all vertices have even degree, graph is already valid
25        if (oddDegreeVertices.size() == 0) {
26            return true;
27        }
28      
29        // Case 1: Exactly 2 vertices with odd degree
30        // We need to add 1 edge between them or through an intermediate vertex
31        if (oddDegreeVertices.size() == 2) {
32            int vertexA = oddDegreeVertices[0];
33            int vertexB = oddDegreeVertices[1];
34          
35            // If edge doesn't exist between A and B, we can directly connect them
36            if (!adjacencyList[vertexA].count(vertexB)) {
37                return true;
38            }
39          
40            // Edge exists between A and B, try to find intermediate vertex C
41            // We need edges A-C and C-B where both don't exist
42            for (int vertexC = 1; vertexC <= n; ++vertexC) {
43                if (vertexA != vertexC && vertexB != vertexC && 
44                    !adjacencyList[vertexA].count(vertexC) && 
45                    !adjacencyList[vertexC].count(vertexB)) {
46                    return true;
47                }
48            }
49            return false;
50        }
51      
52        // Case 2: Exactly 4 vertices with odd degree
53        // We need to add 2 edges to make all 4 vertices have even degree
54        if (oddDegreeVertices.size() == 4) {
55            int vertexA = oddDegreeVertices[0];
56            int vertexB = oddDegreeVertices[1];
57            int vertexC = oddDegreeVertices[2];
58            int vertexD = oddDegreeVertices[3];
59          
60            // Try all possible pairings: (A-B, C-D), (A-C, B-D), (A-D, B-C)
61            // Pairing 1: Connect A with B and C with D
62            if (!adjacencyList[vertexA].count(vertexB) && 
63                !adjacencyList[vertexC].count(vertexD)) {
64                return true;
65            }
66          
67            // Pairing 2: Connect A with C and B with D
68            if (!adjacencyList[vertexA].count(vertexC) && 
69                !adjacencyList[vertexB].count(vertexD)) {
70                return true;
71            }
72          
73            // Pairing 3: Connect A with D and B with C
74            if (!adjacencyList[vertexA].count(vertexD) && 
75                !adjacencyList[vertexB].count(vertexC)) {
76                return true;
77            }
78          
79            return false;
80        }
81      
82        // For any other number of odd degree vertices (1, 3, or > 4)
83        // It's impossible to make all vertices even with at most 2 edges
84        return false;
85    }
86};
87
1function isPossible(n: number, edges: number[][]): boolean {
2    // Build adjacency list using Set for O(1) lookup
3    // Graph is 1-indexed (vertices from 1 to n)
4    const adjacencyList: Set<number>[] = Array(n + 1)
5        .fill(null)
6        .map(() => new Set<number>());
7  
8    // Populate the adjacency list with undirected edges
9    for (const edge of edges) {
10        const vertex1 = edge[0];
11        const vertex2 = edge[1];
12        adjacencyList[vertex1].add(vertex2);
13        adjacencyList[vertex2].add(vertex1);
14    }
15  
16    // Find all vertices with odd degree
17    const oddDegreeVertices: number[] = [];
18    for (let vertex = 1; vertex <= n; vertex++) {
19        if (adjacencyList[vertex].size % 2 === 1) {
20            oddDegreeVertices.push(vertex);
21        }
22    }
23  
24    // If all vertices have even degree, graph is already valid
25    if (oddDegreeVertices.length === 0) {
26        return true;
27    }
28  
29    // Case 1: Exactly 2 vertices with odd degree
30    // We need to add 1 edge between them or through an intermediate vertex
31    if (oddDegreeVertices.length === 2) {
32        const vertexA = oddDegreeVertices[0];
33        const vertexB = oddDegreeVertices[1];
34      
35        // If edge doesn't exist between A and B, we can directly connect them
36        if (!adjacencyList[vertexA].has(vertexB)) {
37            return true;
38        }
39      
40        // Edge exists between A and B, try to find intermediate vertex C
41        // We need edges A-C and C-B where both don't exist
42        for (let vertexC = 1; vertexC <= n; vertexC++) {
43            if (vertexA !== vertexC && 
44                vertexB !== vertexC && 
45                !adjacencyList[vertexA].has(vertexC) && 
46                !adjacencyList[vertexC].has(vertexB)) {
47                return true;
48            }
49        }
50        return false;
51    }
52  
53    // Case 2: Exactly 4 vertices with odd degree
54    // We need to add 2 edges to make all 4 vertices have even degree
55    if (oddDegreeVertices.length === 4) {
56        const vertexA = oddDegreeVertices[0];
57        const vertexB = oddDegreeVertices[1];
58        const vertexC = oddDegreeVertices[2];
59        const vertexD = oddDegreeVertices[3];
60      
61        // Try all possible pairings: (A-B, C-D), (A-C, B-D), (A-D, B-C)
62      
63        // Pairing 1: Connect A with B and C with D
64        if (!adjacencyList[vertexA].has(vertexB) && 
65            !adjacencyList[vertexC].has(vertexD)) {
66            return true;
67        }
68      
69        // Pairing 2: Connect A with C and B with D
70        if (!adjacencyList[vertexA].has(vertexC) && 
71            !adjacencyList[vertexB].has(vertexD)) {
72            return true;
73        }
74      
75        // Pairing 3: Connect A with D and B with C
76        if (!adjacencyList[vertexA].has(vertexD) && 
77            !adjacencyList[vertexB].has(vertexC)) {
78            return true;
79        }
80      
81        return false;
82    }
83  
84    // For any other number of odd degree vertices (1, 3, or > 4)
85    // It's impossible to make all vertices even with at most 2 edges
86    return false;
87}
88

Time and Space Complexity

Time Complexity: O(n + m)

The algorithm consists of the following operations:

  • Building the adjacency list by iterating through all edges: O(m) where m is the number of edges
  • Finding vertices with odd degree by iterating through the graph dictionary: O(n) where n is the number of nodes (at most n vertices in the graph)
  • When there are 2 vertices with odd degree, checking if they're connected takes O(1), and the any() function iterates through all nodes from 1 to n+1, checking set membership operations which are O(1) each, resulting in O(n)
  • When there are 4 vertices with odd degree, we perform constant number of set membership checks: O(1)

The dominant operations are building the graph O(m) and the potential iteration through all nodes O(n), giving us a total time complexity of O(n + m).

Space Complexity: O(n + m)

The space usage includes:

  • The adjacency list g which stores all edges as sets: each edge (a, b) is stored twice (once for each direction), requiring O(m) space for all edges
  • Each vertex can appear in the graph, requiring O(n) space for the dictionary keys
  • The list vs stores at most 4 vertices with odd degree: O(1)

The adjacency list dominates the space usage with O(n + m) total space complexity.

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Incorrectly handling isolated nodes

A critical pitfall is not accounting for nodes that have no edges at all (isolated nodes). These nodes have degree 0 (even) and won't appear in the adjacency list if we only build it from the edges.

Problem Example:

n = 5, edges = [[1,2], [3,4]]
# Node 5 is isolated with degree 0
# Our current code would miss node 5 entirely

In the case of 2 odd-degree nodes, when searching for an intermediate vertex, the code iterates from 1 to n+1, but if a node doesn't exist in the adjacency list (like node 5), checking vertex_a not in adjacency_list[intermediate_vertex] would always return True since adjacency_list[5] would be an empty set.

Solution: Ensure isolated nodes are properly handled by either:

  • Pre-populating the adjacency list with all nodes
  • Being careful when checking for non-existent edges
# Initialize all nodes in the graph first
adjacency_list = {i: set() for i in range(1, n + 1)}
for node_a, node_b in edges:
    adjacency_list[node_a].add(node_b)
    adjacency_list[node_b].add(node_a)

2. Self-loop confusion when checking intermediate vertices

When looking for an intermediate vertex in the 2-node case, there's a subtle issue if we're not careful about the condition check.

Problem Example:

# Current check:
if (vertex_a not in adjacency_list[intermediate_vertex] and 
    intermediate_vertex not in adjacency_list[vertex_b]):

This check has an asymmetry - it checks if vertex_a is not in intermediate_vertex's neighbors, but then checks if intermediate_vertex is not in vertex_b's neighbors. While this works due to the undirected nature of the graph, it's confusing and error-prone.

Solution: Use consistent checking pattern:

if (vertex_a not in adjacency_list[intermediate_vertex] and 
    vertex_b not in adjacency_list[intermediate_vertex]):

3. Not considering the intermediate vertex could be one of the odd-degree vertices

When we have 2 odd-degree vertices and need to find an intermediate vertex, we might accidentally allow one of the odd-degree vertices to be the intermediate vertex, which would create a self-loop.

Problem Example: If vertex_a = 1 and vertex_b = 2, and we check intermediate_vertex = 1, we'd be trying to add edges (1,1) and (1,2), where (1,1) is a self-loop.

Solution: Explicitly exclude the odd-degree vertices from being intermediate vertices:

for intermediate_vertex in range(1, n + 1):
    if intermediate_vertex in {vertex_a, vertex_b}:
        continue  # Skip if it's one of the odd-degree vertices
    if (vertex_a not in adjacency_list[intermediate_vertex] and 
        vertex_b not in adjacency_list[intermediate_vertex]):
        return True
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Which algorithm is best for finding the shortest distance between two points in an unweighted graph?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More