LeetCode Question 2

Question 2

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

题目分析

输入:本题的输入是两个自然数,每个数都逆序放在链表里,比如例子中的 (2 -> 4 -> 3)实际上是342。并且默认除了0以外,自然数的不会以0作为开头,也就是说342不会以 (2 -> 4 -> 3 – > 0)的形式存在(即0342)。
处理:函数作用是将两个链表中的自然数相加,同样除了0以外的数不以0开头,比如例子中342+465=807。
输出:将结果以链表形式输出,即把结果807用链表 (7 -> 0 -> 8)输出。

检验设置

TDD(Test-driven development) analysis:

1. 最简单的情形是两个数位数相同且加和不进位:
l1 =  (2 -> 4 -> 3), l2 =  (3 -> 2 -> 5)
>>>  (5 -> 6 -> 8)
2. 同样加和不进位的情形下,使两个数位数不同:
l1 =  (2 -> 4 -> 3), l2 =  (2 -> 5)
>>> (4 -> 9 -> 3)
3. 位数相/不同的两个数加和进位,但保持结果的链表长度等于输入两个链表的最大长度:
l1 =  (2 -> 4 -> 3), l2 =  (3 -> 7 -> 5)
>>> (5 -> 1 -> 9)
4. 位数相/不同的两个数加和进位,且结果的链表长度大于输入两个链表的最大长度:
l1 =  (2 -> 4 -> 3), l2 =  (3 -> 7 -> 6)
>>> (5 - > 1 -> 0 -> 1)
5. 特殊情况,两个数都是0:
l1 =  (0), l2 =  (0)
>>> (0)

链表的结构要事先定义好,这里与题目给出的class一致,链表当中的值用val表示,连接用next表示,如下

class ListNode(object):
    def __init__(self, x):
        self.val = x
        self.next = None

为了方便检验,还需要写两个函数分别实现创建链表和显示链表的功能。

#根据输入的列表创建链表,列表与链表的顺序一致
#l = [2, 4, 3](代表自然数342)
#>>> (2 - > 4 - > 3)
def createListNode(l):
    head = temp = ListNode(0)
    #先设置一个head,像基因编码一样找一个生成器
    for i in l:
        temp.next = ListNode(i)
        #将输入list中的每个元素变成一个链表结构
        temp = temp.next
    return head.next
    #返回需要的链表,扔掉编码开始的head
#显示链表
#l = (2 - > 4 - > 3)
#>>> 243
def printListNode(ln):
    while ln:
        print(ln.val, end = '')
        #按照链表顺序输出value,同时不换行
        ln = ln.next
    print('')
    #换行,使得后续输出的结果在新一行出现,容易辨认

1.直接解法(Python3):

既然是在自然数加法上复合了链表,直接的解法是单独处理链表与加和:先把输入的链表转换为自然数,再把加和后的结果转换为链表。

def addTwoNumbers(l1, l2):
    def lnToInt(ln):
    #将链表(缩写为名称的ln,listnode)转化为整型数(缩写为名称的int)
        num = count = 0
        #num代表转化后的整型数,count代表数位
        while ln:
            num += ln.val * pow(10, count)
            #把链表每个数字放到整型数对应的位数上,所以乘以了10的count次方
            count += 1
            ln = ln.next
        return num

    def intToLn(n):
    #将整型数转换为链表
        head = temp = ListNode(0)
        #先设置一个head,像基因编码一样找一个生成器
        if n == 0:
            return head
        #如果整型数为0,直接返回链表(0),这也是head设置为ListNode(0)的好处
        while n:
            temp.next = ListNode(n % 10)
            #找到余数,也就是链表对应的1位数元素
            temp = temp.next
            n = n // 10
            #获得剩下未转换为链表的整型数
        return head.next
    sum = lnToInt(l1) + lnToInt(l2)
    return intToLn(sum)

时间复杂度O(n),空间复杂度O(n),链表与加和分开处理的思考难度低,但是效率被浪费在链表和自然数的相互转化中,可以继续优化,方向是赋予链表加和能力。

2.链表的加法运算:

实现链表加法的核心有两个:进位问题和链表长度变化问题。这两个问题都是由链表表示自然数的规定引发的,规定中要求每一个链表元素都只能是1位数,如果加和出现两位数是在链表中间,就是进位问题,在链表末端就是长度变化问题。解决方案是用一个变量来记录进位的数字,比如用carry来记录,链表的元素取加和后结果的个位数,比如,5+7=12,这时链表的元素取个位数2,carry=1,当下一位进行计算的时候把carry的值加入。

def addTwoNumbersNode(l1, l2):
    head = temp = ListNode(0)
    #先设置一个head,像基因编码一样找一个生成器
    carry = 0
    #用来存放加和是两位数时的十位数
    while l1 or l2 or carry:
    #只有当l1和l2是null,且carry是0的时候才停止生成链表
        value1 = l1.val if l1 else 0
        value2 = l2.val if l2 else 0
        sum = value1 + value2 + carry
        temp.next = ListNode(sum % 10)
        #链表的元素是加和值的个位数
        carry = sum // 10
        #carry的值是加和值的十位数
        temp = temp.next
        l1 = l1.next if l1 else None
        l2 = l2.next if l2 else None
    return head.next

时间复杂度O(n),空间复杂度O(n),实现了链表和加法的功能组合。

    原文作者:Sisyphus235
    原文地址: https://www.jianshu.com/p/f83698301cff
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞