leetcode刷题——Q2两数相加

156 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第1天,点击查看活动详情

题目描述:Q2:两数相加

给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。

请你将两个数相加,并以相同形式返回一个表示和的链表。

你可以假设除了数字 0 之外,这两个数都不会以 0 开头。

示例 1:

输入:l1 = [2,4,3], l2 = [5,6,4]
输出:[7,0,8]
解释:342 + 465 = 807.

示例 2:

输入:l1 = [0], l2 = [0]
输出:[0]

示例 3:

输入:l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9]
输出:[8,9,9,9,0,0,0,1]

提示:

  • 每个链表中的节点数在范围 [1, 100]
  • 0 <= Node.val <= 9
  • 题目数据保证列表表示的数字不含前导零

思路 & CODE

1. 直接相加

既然题目都说了两数相加,那就直接把两个数加起来,亏它是一道medium题。再看一下链表范围是[1, 100],也就是说链表中数字最大值是10^100,而int最大值是2^32long最大值是2^64,既然这些变量都不行,那就直接上大将BigDecimal,说干就干

public ListNode addTwoNumbers02(ListNode l1, ListNode l2) {
    ListNode resNode = null;
    ListNode headNode = resNode;
    String num1 = "";
    String num2 = "";
    while (l1 != null) {
        num1 += l1.val;
    }
    while (l2 != null) {
        num2 += l2.val;
    }
    BigDecimal bNum1 = new BigDecimal(num1);
    BigDecimal bNum2 = new BigDecimal(num2);
    bNum1 = bNum1.add(bNum2);
    for (char c : bNum1.toPlainString().toCharArray()) {
        if (resNode == null) {
            resNode = new ListNode(c);
        } else {
            resNode.next = new ListNode(c);
        }
    }
    return headNode;
}

然而现实是残酷的,虽然没报溢出,但是超时了。

2. 维护一个加法进位变量

题目已经给出了提示,每位数字都是按照逆序以链表的方式存储,这就简单了呀,每次取这两个链表的头节点出来进行相加,然后把相加的数拼到新链表上就行了,唯一需要注意的就是十进制相加需要进位,我们只需要添加一个变量来维护进位就可以了,开干:

public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        // 用来维护返回的链表
        ListNode resNode = null;
        // 用来存储头节点
        ListNode headNode = null;
        // 进位变量,只会有两个值0、1
        int carryNum = 0;
        while (l1 != null || l2 != null) {
            // 如果链表节点是null就取0,因为另一个链表肯定有值的
            int l1Val = l1 == null ? 0 : l1.val;
            int l2Val = l2 == null ? 0 : l2.val;
            // 链表节点
            l1 = l1 == null ? null : l1.next;
            l2 = l2 == null ? null : l2.next;
            // 链表1 + 链表2 + 进位
            int sum = l1Val + l2Val + carryNum;
            // 维护新链表节点的值
            int nowVal = 0;
            // 处理进位和本次要添加到链表中的值
            if (sum >= 10) {
                carryNum = 1;
                nowVal = sum - 10;
            } else {
                carryNum = 0;
                nowVal = sum;
            }
            // 维护新的链表
            if (resNode == null) {
                resNode = new ListNode(nowVal);
                headNode = resNode;
            } else {
                resNode.next = new ListNode(nowVal);
                resNode = resNode.next;
            }
        }
        // 如果最后进位是1说明链表还需要加一个节点
        if (carryNum == 1) {
            resNode.next = new ListNode(1);
        }
        return headNode;
    }

一运行,成功击败了100%的提交,虽然成功ac了,但是这种代码越看越丑陋,if写的也太多了,不符合我智慧的人社!

进一步优化

看大佬题解找到了如下代码:

public ListNode addTwoNumbers03(ListNode l1, ListNode l2) {
        ListNode root = new ListNode(0);
        ListNode cursor = root;
        int carry = 0;
        while(l1 != null || l2 != null || carry != 0) {
            int l1Val = l1 != null ? l1.val : 0;
            int l2Val = l2 != null ? l2.val : 0;
            int sumVal = l1Val + l2Val + carry;
            carry = sumVal / 10;

            ListNode sumNode = new ListNode(sumVal % 10);
            cursor.next = sumNode;
            cursor = sumNode;

            if(l1 != null) l1 = l1.next;
            if(l2 != null) l2 = l2.next;
        }
        return root.next;
    }
  • 1)dummayHead解决头节点何时创建问题
//======dummayHead优化==================
ListNode root = new ListNode(0);
...
...
return root.next
    
    
//=======我的代码========================
// 如果头节点是null,就创建头节点
if (resNode == null) {
    resNode = new ListNode(nowVal);
    headNode = resNode;
} else {
    // 创建新的节点
    resNode.next = new ListNode(nowVal);
    resNode = resNode.next;
}

这么大一段代码直接就给干掉了,之前也学习过dummyHead,但是这里没想到用,以后纠结头节点怎么创建的时候,可以直接套公式——直接使用一个dummyHead

  • 2)除法取余的秒用
//========优化后的代码
int sumVal = l1Val + l2Val + carry;
carry = sumVal / 10;
ListNode sumNode = new ListNode(sumVal % 10);


//========我的代码========================
int sum = l1Val + l2Val + carryNum;
// 维护新链表节点的值
int nowVal = 0;
// 处理进位和本次要添加到链表中的值
if (sum >= 10) {
    carryNum = 1;
    nowVal = sum - 10;
} else {
    carryNum = 0;
    nowVal = sum;
}

我用了一堆if判断和10进行比较来算出本次节点的值以及是否进位,但是大佬直接一个除法、一个取余就把问题解决了。以后发生数值运算除法和取余考虑的优先级应该是最高等级。

  • 3)if判断和三元表达式
//=======if判断
if(l1 != null) l1 = l1.next;
if(l2 != null) l2 = l2.next;

//=======三元表达式
l1 = l1 == null ? null : l1.next;
l2 = l2 == null ? null : l2.next;

有时候三元表达式并不比if判断简洁

总结

1. 虚拟头节点

以后纠结头节点怎么创建的时候,可以直接套公式——直接使用一个dummyHead来解决何时创建头节点的问题,最后可以return head.next返回正常头节点

2. 除法和取余

发生数值运算时,除法取余需要被考虑的优先级应该是最高的。

3. 三元表达式有时候并不比if判断简洁