Start learning free

LESSON 306 OF 415 · LINKED LISTS

Add Two Numbers in Swift: Digit-by-Digit Addition with a Carry

Practice O(n) Read the full explanation

You are given two non-empty linked lists representing two non-negative integers. The digits are stored in reverse order, and each of their nodes contains a single digit. Add the two numbers and return the sum as a linked list.

You may assume the two numbers do not contain any leading zero, except the number 0 itself.

Example 1:

Input: list1 = [2, 4, 3], list2 = [5, 6, 4]
Output: [7, 0, 8]
Explanation: 342 + 465 = 807


Example 2:

Input: list1 = [9,9,9,9,9,9,9], list2 = [9,9,9,9]
Output: [8,9,9,9,0,0,0,1]
Explanation: 9999999 + 9999 = 10009998

STEP 1 OF 12
    Ctrl + Enter to check

    SWIFT · LINKED LISTS

    Add Two Numbers in Swift, explained

    Problem overview

    The method addTwoNumbers receives two non-empty linked lists, list1 and list2. Each list is a non-negative integer with one digit per node, stored in reverse order: the first node holds the ones digit, the next the tens digit, and so on. The method returns their sum as a list in the same format. The numbers have no leading zeros, except for the number 0 itself.

    In the first example, [2, 4, 3] is 342 and [5, 6, 4] is 465. Their sum is 807, so the result is [7, 0, 8]. In the second example, [9, 9, 9, 9, 9, 9, 9] plus [9, 9, 9, 9] is 10,009,998, which comes back as [8, 9, 9, 9, 0, 0, 0, 1]: the result has one more digit than the longer input.

    Approach: do it the way you add on paper

    Reverse order is convenient. Written addition starts with the ones column and moves left, and the lists already start with the ones digit. So the code can walk both lists from the head, add one column at a time, and build the result from its least significant digit, which is also the order the answer needs.

    For each column, the sum is the digit of the first list, plus the digit of the second list, plus the carry from the previous column. A list that has run out contributes 0. The new digit is that sum modulo 10, and the new carry is 1 if the sum is above 9, otherwise 0. The largest possible column sum is 9 + 9 + 1 = 19, so the carry is never more than 1.

    The loop must continue while either list has digits left or there is a carry. The carry condition is what produces the extra digit at the end, as in 5 + 5, which is [0, 1].

    Converting the lists to numbers and adding them is a trap. Lists can be long enough that the value doesn't fit in an Int, which has 64 bits in Swift, so working column by column is the safe approach.

    Step by step

    1. Create the cursors. node1 and node2 start at the two heads. They are var, because they move. Swift lets one var declare both, separated by a comma.
    2. Create a dummy head head, and a tail that starts on it. The dummy removes the special case for the first result node. head never changes, so it is a let, and tail moves, so it is a var.
    3. Start the carry at 0. The first column has no carry coming in.
    4. Loop while there is work. The condition is node1 != nil || node2 != nil || carry > 0. Swift conditions must be Bool, so each part is an explicit comparison.
    5. Seed the column sum with the carry: var digitSum = carry.
    6. Take a digit from the first list if there is one. Add node1!.val and move node1 to its next. The ! is safe because the if just checked for nil.
    7. Do the same for the second list with a separate if, not an else. Both lists can have a digit in the same column.
    8. Compute the carry with a ternary: digitSum > 9 ? 1 : 0.
    9. Reduce the sum to one digit with digitSum %= 10.
    10. Append a node holding the digit to tail.next, then move tail onto it.
    11. Return head.next, skipping the dummy, which holds a meaningless 0.

    The solution in Swift

    class Solution {
        func addTwoNumbers(_ list1: ListNode?, _ list2: ListNode?) -> ListNode? {
            var node1 = list1, node2 = list2
            let head = ListNode(); var tail = head
            var carry = 0
            while node1 != nil || node2 != nil || carry > 0 {
                var digitSum = carry
                if node1 != nil {
                    digitSum += node1!.val
                    node1 = node1!.next
                }
                if node2 != nil {
                    digitSum += node2!.val
                    node2 = node2!.next
                }
                carry = digitSum > 9 ? 1 : 0
                digitSum %= 10
                tail.next = ListNode(digitSum)
                tail = tail.next!
            }
            return head.next
        }
    }
    

    Swift details worth knowing:

    • A Bool is not an Int. In Swift, carry = digitSum > 9 doesn't compile: the compiler says it cannot assign a value of type Bool to type Int. The ternary ? 1 : 0 converts the comparison into a number.
    • let head = ListNode() and then tail.next = .... let makes the variable constant, not the object. ListNode is a class, so head can't be reassigned, but its properties can change. tail starts as the same object and is reassigned as it moves.
    • Two optionals, two unwraps. node1 is a ListNode?. After the nil check the code still writes node1!, because Swift doesn't narrow the type of a var in this style of check. tail.next is also optional, so tail = tail.next! is needed even though the line above just assigned it.
    • %=. It replaces the variable with the remainder after division by 10. 12 % 10 is 2, and 8 % 10 is 8.
    • Overflow traps in Swift. Integer arithmetic that overflows Int crashes the program instead of wrapping. The column sums here stay at 19 or less, so the code never gets close, but a conversion of long lists to one big Int would crash.

    Complexity

    Let m and n be the lengths of the lists.

    Time: O(max(m, n)). Each loop iteration consumes one digit from at least one list, or handles the last carry, and each iteration costs constant time.

    Space: O(max(m, n)) for the result. The new list has at most max(m, n) + 1 nodes. Apart from the output, the code uses only a few variables, and it leaves the input lists unchanged.

    Edge cases and common mistakes

    • Different lengths. For [1] and [9, 9] (1 + 99) the result is [0, 0, 1]. The shorter list simply stops contributing.
    • A final carry. [5] plus [5] gives [0, 1]. Without carry > 0 in the loop condition, the last 1 would be dropped.
    • Zeros. [0] plus [0] gives [0]. The loop runs once for the single column, produces 0, and then stops because there is no carry.
    • Using else for the second list. Then each column would take a digit from only one list, and the sums would be wrong.
    • Returning head. The dummy's 0 would show up as an extra leading node. Return head.next.

    Testing

    • [2, 4, 3] and [5, 6, 4] give [7, 0, 8].
    • [0] and [0] give [0].
    • [9, 9, 9, 9, 9, 9, 9] and [9, 9, 9, 9] give [8, 9, 9, 9, 0, 0, 0, 1].
    • [5] and [5] give [0, 1].
    • [1] and [9, 9] give [0, 0, 1].
    Keep going with SwiftAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free