算法2. 两数相加

176 阅读3分钟

「这是我参与2022首次更文挑战的第26天,活动详情查看:2022首次更文挑战」。

一、题目

二、我的解答

第一次解答:

报错日志,第1个测试用例:

第二次解答:

报错日志,第32个测试用例:

第三次解答:

报错日志,第1565个测试用例:

第四次解答:

三、系统解答

方法一:模拟

思路及算法

复杂度分析

一、题目

难度中等

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

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

你可以假设除了数字 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

• 题目数据保证列表表示的数字不含前导零

二、我的解答

第一次解答:

我一开始的写法,相当糟糕。题意没注意看。题目说了,这个数的位数在 [1, 100]之间,不一定几位,我写死了三位,辣鸡仔。运行代码,系统只跑了三位数的测试用例,所以我当然没问题。靠

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        if(l1==null||l2==null){
            return null;
        }
        int num1=l1.next.next.val*100+l1.next.val*10+l1.val;
        int num2=l2.next.next.val*100+l2.next.val*10+l2.val;
        int num3=num1+num2;
        int val3=num3/100;
        ListNode l3_3=new ListNode(val3);
         int val2=(num3-val3*100)/10;
        ListNode l3_2=new ListNode(val2,l3_3);
         int val1=num3-val3*100-val2*10;
        ListNode l3=new ListNode(val1,l3_2);
        return l3;
    }
}

报错日志,第1个测试用例:

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

实际:异常终止

原因:16line,我这个小垃圾写死了三位,l1.next为null,我还去l1.next.next,当然会空指针异常了

第二次解答:

我的思路是先把两个链表转成整型加起来再转回去,操,结果被[1,9,9,9,9,9,9,9,9,9]教做人了。以下是代码,这段代码我还以为自己写得很风流,辣鸡仔。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
   public  ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        if (l1 == null || l2 == null) {
            return null;
        }
        List num1List = new ArrayList<>();
        ListNode temp1 = l1;
        while (temp1 != null) {
            num1List.add(temp1.val);
            temp1 = temp1.next;
        }
        int num1 = 0;
        for (int i = 0; i < num1List.size(); i++) {
            num1 = (int) (num1List.get(i) * Math.pow(10, i) + num1);
        }
        List num2List = new ArrayList<>();
        ListNode temp2 = l2;
        while (temp2 != null) {
            num2List.add(temp2.val);
            temp2 = temp2.next;
        }
        int num2 = 0;
        for (int i = 0; i < num2List.size(); i++) {
            num2 = (int) (num2List.get(i) * Math.pow(10, i) + num2);
        }
        int num3 = num1 + num2;
        ListNode l3 = null;
        ListNode next = null;
        String num3String = num3 + "";
        for (int i = 0; i < num3String.length(); i++) {
            int val = Integer.parseInt(num3String.charAt(i) + "");
            ListNode node = new ListNode(val);
            node.next = next;
            next = node;
            if (i == (num3String.length() - 1)) {
                l3 = node;
            }
        }
        return l3;
    }
}

报错日志,第32个测试用例:

输入:l1 =[9], l2 = [1,9,9,9,9,9,9,9,9,9]****预期输出:[000000000001]

实际:异常终止

原因:我们算出来的num3String 是-2147483640,执行到Integer.parseInt("-");当然报错啦。

但是关键原因是我们为什么会算出一个负的数,害,还不是因为超出了数据类型的范围。

int的范围是 4*8 = 32 bit -2^32~2^31 (-2147483648 ~ 2147483647),你给他一个大于他的数强转,精度当然会出问题了。

第三次解答:

聪明的我又换成了long。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
 public  ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        if (l1 == null || l2 == null) {
            return null;
        }
        List num1List = new ArrayList<>();
        ListNode temp1 = l1;
        while (temp1 != null) {
            num1List.add(temp1.val);
            temp1 = temp1.next;
        }
        long num1 = 0;
        for (int i = 0; i < num1List.size(); i++) {
            num1 = (long) (num1List.get(i) * Math.pow(10, i) + num1);
        }
        List num2List = new ArrayList<>();
        ListNode temp2 = l2;
        while (temp2 != null) {
            num2List.add(temp2.val);
            temp2 = temp2.next;
        }
        long num2 = 0;
        for (int i = 0; i < num2List.size(); i++) {
            num2 = (long) ((num2List.get(i) * Math.pow(10, i)) + num2);
        }
        long num3 = num1 + num2;
        ListNode l3 = null;
        ListNode next = null;
        String num3String = num3 + "";
        for (int i = 0; i < num3String.length(); i++) {
            int val = Integer.parseInt(num3String.charAt(i) + "");
            ListNode node = new ListNode(val);
            node.next = next;
            next = node;
            if (i == (num3String.length() - 1)) {
                l3 = node;
            }
        }
        return l3;
    }
}

报错日志,第1565个测试用例:

输入:l1 =[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], l2 =[5,6,4]****预期输出:?

实际:异常终止

原因:操,又教做人了。题目不能认真点看吗?

每个链表中的节点数在范围 [1, 100]

看这意思岂不是我没办法用其他数据类型,哪有可以100位的数据类型,bigInt也不行啊。

看样子是方法没用对

第四次解答:

换个思路,不转类型了直接加。

最后我的解答,终于通过了。但是我看了官方解答后,才知道自己多么蠢。

class Solution {
 public  ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        if (l1 == null || l2 == null) {
            return null;
        }
        List num1List = new ArrayList<>();
        ListNode temp1 = l1;
        while (temp1 != null) {
            num1List.add(temp1.val);
            temp1 = temp1.next;
        }
        List num2List = new ArrayList<>();
        ListNode temp2 = l2;
        while (temp2 != null) {
            num2List.add(temp2.val);
            temp2 = temp2.next;
        }
        int length=num1List.size()>num2List.size()?num1List.size():num2List.size();
        boolean add1=false;
        ListNode next=null;
        ListNode l3 = null;
        ListNode node=null;
        //可能会进位,所以多留一位=length
        for (int i = 0; i <=length; i++) {
            int temp=(num1List.size()<(i+1)?0:num1List.get(i))+(num2List.size()<(i+1)?0:num2List.get(i));
            if (add1){
                temp=temp+1;
            }
            if (temp>=10){
                add1=true;
            }else {
                add1=false;
            }
            if (i==length&&temp==0){
                return l3;
            }
            next = new ListNode(temp%10);
            if (node==null){
                l3=next;
            }else {
                node.next=next;
            }
            node=next;
        }
        return l3;
    }}

三、系统解答

方法一:模拟

思路及算法

由于输入的两个链表都是逆序存储数字的位数的,因此两个链表中同一位置的数字可以直接相加。

我们同时遍历两个链表,逐位计算它们的和,并与当前位置的进位值相加。具体而言,如果当前两个链表处相应位置的数字为 n1,n2n1,n2,进位值为carry,则它们的和为 n1+n2+carry;其中,答案链表处相应位置的数字为 (n1+n2+carry)mod10,而新的进位值为:

如果两个链表的长度不同,则可以认为长度短的链表的后面有若干个 00 。

此外,如果链表遍历结束后,有carry>0,还需要在答案链表的后面附加一个节点,节点的值为 carry。

注释:思路和我一样,代码比我写的简单太多了吧

代码

class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        ListNode head = null, tail = null;
        int carry = 0;
        while (l1 != null || l2 != null) {
            int n1 = l1 != null ? l1.val : 0;
            int n2 = l2 != null ? l2.val : 0;
            int sum = n1 + n2 + carry;
            if (head == null) {
                head = tail = new ListNode(sum % 10);
            } else {
                tail.next = new ListNode(sum % 10);
                tail = tail.next;
            }
            carry = sum / 10;
            if (l1 != null) {
                l1 = l1.next;
            }
            if (l2 != null) {
                l2 = l2.next;
            }
        }
        if (carry > 0) {
            tail.next = new ListNode(carry);
        }
        return head;
    }
}

复杂度分析

• 时间复杂度:O(max(m,n)),其中 m 和 n 分别为两个链表的长度。我们要遍历两个链表的全部位置,而处理每个位置只需要 O(1) 的时间。

• 空间复杂度:O(1)。注意返回值不计入空间复杂度。