6
\$\begingroup\$

The task is taken from LeetCode. I would appreciate an assessment of whether my coding style follows good practices, and if there are any habits or patterns you would find concerning when working together on a codebase.

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 contain a single digit. Add the two numbers and return it as a linked list.

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

Example:

Input: (2 -> 4 -> 3) + (5 -> 6 -> 4)

Output: 7 -> 0 -> 8

Explanation: 342 + 465 = 807.

My solution (Python3):

# Definition for singly-linked list provided by the platform.
class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

# My solution 
# (Leetcode doesn't really let you put things in an __init__ method in a defined way 
# (we don't know if a new instance of Solution will be created for each test case), 
# so I didn't have one).
class Solution:
    def addTwoNumbers(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]:
        self.sumHead: ListNode = ListNode(0)
        self.sumLinkedList: ListNode = self.sumHead

        self.carry: int = 0
        while l1 and l2:
            # both numbers still have digits

            # Sanity Check
            assert self.carry in [0, 1]

            self.addDigitAndProcessCarry(l1.val + l2.val + self.carry)
            
            # move to the next node for both numbers
            l1 = l1.next
            l2 = l2.next

            # move to the next node for the sum
            if l1 or l2 or self.carry:
                self.sumLinkedList.next = ListNode(0)
                self.sumLinkedList = self.sumLinkedList.next

        for linkedListNotConsumed in [l1, l2]:
            # only one linked list has more digits to be processed
            while linkedListNotConsumed:
                # Sanity Check
                assert self.carry in [0, 1]

                self.addDigitAndProcessCarry(linkedListNotConsumed.val + self.carry)
                
                # move to the next node
                linkedListNotConsumed = linkedListNotConsumed.next

                if linkedListNotConsumed or self.carry:
                    self.sumLinkedList.next = ListNode(0)
                    self.sumLinkedList = self.sumLinkedList.next
            
        if self.carry:
            # Sanity Check
            assert self.carry in [0, 1]

            self.sumLinkedList.val = 1

        return self.sumHead
    
    def addDigitAndProcessCarry(self, value: int) -> None:
        if value >= 10:
            self.sumLinkedList.val = value - 10
            self.carry = 1
        else:
            self.sumLinkedList.val = value
            self.carry = 0

\$\endgroup\$

2 Answers 2

5
\$\begingroup\$

Naming

The PEP 8 style guide recommends snake_case for function and variable names. I realize the LeetCode site requires you to give the primary function those names, but you would otherwise name the function as add_two_numbers.

Also, addDigitAndProcessCarry would be add_digit_and_process_carry

For variables, sumHead would be sum_head, etc.

Comments

Many of your comments are helpful. However, this comment is not needed:

# My solution 
# (Leetcode doesn't really let you put things in an __init__ method in a defined way 
# (we don't know if a new instance of Solution will be created for each test case), 
# so I didn't have one).

Again, I realize you added this to explain the LeetCode restrictions.

Documentation

PEP 8 recommends adding docstrings for functions. The docstring would decscribe what the function does and how it does it, similar to the description of the problem in the question:

def add_two_numbers(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]:
    """
    Given two non-empty linked lists representing two non-negative integers...
    """

DRY

I see this code repeated 3 times:

assert self.carry in [0, 1]

I'm not sure you need to do that check in that many places, but if you do, you could create a helper function for the check to reduce the repetition.

\$\endgroup\$
6
\$\begingroup\$

Within the confines of Leetcode's requirements, this is about as straightforward a solution as can be hoped for. A few style suggestions have been pointed out by toolic.

The following code can be simplified a bit:

    def addDigitAndProcessCarry(self, value: int) -> None:
        if value >= 10:
            self.sumLinkedList.val = value - 10
            self.carry = 1
        else:
            self.sumLinkedList.val = value
            self.carry = 0

If we modulo value by 10, we can assume that is the next digit, and the carry is simply value divided (ensure integer division!) by 10.

    def addDigitAndProcessCarry(self, value: int) -> None:
        self.sumLinkedList.val = value % 10
        self.carry = value // 10

If writing within the confines of Leetcode's Solution class doesn't make sense, you are free to write your own functions and classes outside of it, and then simply wrap your solution in Solution.addTwoNumbers.

LeetCode's data structures

LeetCode's linked list data structure is dumb. It doesn't do nice things like let you iterate over it using built-in functionality in Python. But you don't have to live with that restriction.

You can implement that behavior yourself!

As an example, converting to and from iterators for LeetCode's ListNode:

def list_to_iter(lst):
    while lst:
        yield lst.val
        lst = lst.next

def iter_to_list(i):
    try:
        cur = head = ListNode(next(i))
    except StopIteration:
        return
    
    try:
        while x := next(i):
            cur.next = ListNode(x)
            cur = cur.next
    except StopIteration:
        return head

Note that you might also monkey patch this behavior into ListNode.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ You can get the quotient and remainder in a single operation using the built-in divmod() function. self.carry, self.sumLinkedList.val = divmod(value, 10) \$\endgroup\$ Commented 2 hours ago

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.