LeetCode 1634 - Add Two Polynomials Represented as Linked Lists
This problem asks us to add two polynomials represented as singly linked lists. Each node in the linked list represents a single term of a polynomial, with a coefficient and a power.
Difficulty: 🟡 Medium
Topics: Linked List, Math, Two Pointers
Solution
Problem Understanding
This problem asks us to add two polynomials represented as singly linked lists. Each node in the linked list represents a single term of a polynomial, with a coefficient and a power. The linked list is sorted in strictly descending order of powers, and terms with zero coefficients do not appear. For example, the polynomial 2x^2 + 4x + 3 would be represented as [[2,2],[4,1],[3,0]].
The inputs are the heads of two such linked lists, poly1 and poly2, and we are to produce a new linked list that represents the sum of these polynomials, also in descending order by power, omitting terms whose coefficient becomes zero.
The constraints tell us that each polynomial can have up to 10,000 terms, coefficients can be very large in magnitude (up to 10^9), and powers can also be very large (up to 10^9). However, the lists are strictly decreasing in power, and no coefficient is initially zero.
Edge cases include completely disjoint powers, terms that cancel each other out producing zero coefficients, and empty polynomials. The problem guarantees that input lists are sorted, which is crucial for an efficient solution.
Approaches
The brute-force approach is to traverse each polynomial and insert each term into a dictionary keyed by power. Then, sum the coefficients for terms with the same power and reconstruct a new linked list in descending order. This works but requires sorting the powers at the end, giving an O(n log n) time complexity and extra space for the dictionary.
The optimal approach leverages the fact that the input lists are already sorted in descending order by power. Using a two-pointer technique, we can traverse both lists simultaneously, comparing powers at each step. If the powers are equal, we sum the coefficients. If one power is greater, we append that term to the result. This preserves the descending order naturally and avoids extra sorting.
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(n log n) | O(n) | Uses a dictionary to sum terms, then sorts powers |
| Optimal | O(n + m) | O(1) | Two-pointer traversal without extra sorting |
Algorithm Walkthrough
- Initialize a dummy node to serve as the head of the result linked list. Use a pointer
currentto track the end of the result list as we build it. - Initialize two pointers,
p1andp2, to the heads ofpoly1andpoly2. - While both
p1andp2are not null:
- Compare the powers
p1.powerandp2.power. - If
p1.power > p2.power, appendp1to the result and movep1forward. - If
p1.power < p2.power, appendp2to the result and movep2forward. - If
p1.power == p2.power, sum the coefficients. If the sum is not zero, create a new node with the summed coefficient and the same power, then append it to the result. Move bothp1andp2forward.
- After the loop, append any remaining nodes from
p1orp2to the result. - Return
dummy.nextas the head of the new polynomial.
Why it works:
The two-pointer technique guarantees that we process all terms in descending order of power. Terms with the same power are combined, and terms with zero coefficients are omitted. The result remains sorted without additional sorting.
Python Solution
# Definition for polynomial singly-linked list.
# class PolyNode:
# def __init__(self, x=0, y=0, next=None):
# self.coefficient = x
# self.power = y
# self.next = next
class Solution:
def addPoly(self, poly1: 'PolyNode', poly2: 'PolyNode') -> 'PolyNode':
dummy = PolyNode()
current = dummy
p1, p2 = poly1, poly2
while p1 and p2:
if p1.power > p2.power:
current.next = PolyNode(p1.coefficient, p1.power)
p1 = p1.next
elif p1.power < p2.power:
current.next = PolyNode(p2.coefficient, p2.power)
p2 = p2.next
else:
summed_coeff = p1.coefficient + p2.coefficient
if summed_coeff != 0:
current.next = PolyNode(summed_coeff, p1.power)
p1 = p1.next
p2 = p2.next
if current.next:
current = current.next
while p1:
current.next = PolyNode(p1.coefficient, p1.power)
current = current.next
p1 = p1.next
while p2:
current.next = PolyNode(p2.coefficient, p2.power)
current = current.next
p2 = p2.next
return dummy.next
This implementation mirrors the algorithm. We use a dummy node to simplify appending. The while loop compares powers to decide which node to append. When powers match, we sum coefficients and only append if nonzero. Remaining nodes are appended afterward.
Go Solution
type PolyNode struct {
Coefficient int
Power int
Next *PolyNode
}
func addPoly(poly1 *PolyNode, poly2 *PolyNode) *PolyNode {
dummy := &PolyNode{}
current := dummy
p1, p2 := poly1, poly2
for p1 != nil && p2 != nil {
if p1.Power > p2.Power {
current.Next = &PolyNode{Coefficient: p1.Coefficient, Power: p1.Power}
p1 = p1.Next
} else if p1.Power < p2.Power {
current.Next = &PolyNode{Coefficient: p2.Coefficient, Power: p2.Power}
p2 = p2.Next
} else {
sum := p1.Coefficient + p2.Coefficient
if sum != 0 {
current.Next = &PolyNode{Coefficient: sum, Power: p1.Power}
}
p1 = p1.Next
p2 = p2.Next
}
if current.Next != nil {
current = current.Next
}
}
for p1 != nil {
current.Next = &PolyNode{Coefficient: p1.Coefficient, Power: p1.Power}
current = current.Next
p1 = p1.Next
}
for p2 != nil {
current.Next = &PolyNode{Coefficient: p2.Coefficient, Power: p2.Power}
current = current.Next
p2 = p2.Next
}
return dummy.Next
}
In Go, the main difference is the use of nil instead of None and explicit struct initialization. The logic and flow are identical to Python.
Worked Examples
Example 1
Input: poly1 = [[1,1]], poly2 = [[1,0]]
Initial pointers: p1=1x^1, p2=1x^0
Compare powers: 1 > 0, append 1x^1
Advance p1 → None
Append remaining p2=1x^0
Output: [[1,1],[1,0]]
Example 2
Input: poly1 = [[2,2],[4,1],[3,0]], poly2 = [[3,2],[-4,1],[-1,0]]
Stepwise:
| p1 | p2 | Action | Result List |
|---|---|---|---|
| 2x^2 | 3x^2 | sum=5 | 5x^2 |
| 4x^1 | -4x^1 | sum=0 | skip |
| 3x^0 | -1x^0 | sum=2 | 2x^0 |
Output: [[5,2],[2,0]]
Example 3
Input: poly1 = [[1,2]], poly2 = [[-1,2]]
Sum of powers 2: 1 + (-1) = 0 → skip
No remaining nodes
Output: []
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(n + m) | Each list is traversed once, where n and m are the lengths of poly1 and poly2 |
| Space | O(1) | Only pointers and result list are used; we do not use extra space proportional to input |
The complexity is optimal given that we must inspect every term once.
Test Cases
# Provided examples
assert addPoly(create_poly([[1,1]]), create_poly([[1,0]])) == [[1,1],[1,0]] # simple addition
assert addPoly(create_poly([[2,2],[4,1],[3,0]]), create_poly([[3,2],[-4,1],[-1,0]])) == [[5,2],[2,0]] # cancellation of middle term
assert addPoly(create_poly([[1,2]]), create_poly([[-1,2]])) == [] # complete