Leetcode刷题系列:两数相加

417 阅读4分钟

这是我参与8月更文挑战的第5天,活动详情查看:8月更文挑战

题目

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

请你将两个数相加,并以相同形式返回一个表示和的链表。 你可以假设除了数字 0 之外,这两个数都不会以 0 开头。

提示:

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

来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/ad…

示例:

示例 1:

image.png

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

示例 2:

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

理解题意:

题目主要内容:

  • 给定两个非空的链表
  • 逆序存储的链表
    • 即 2—>4->3 正序数字为 342
  • 两个数相加后,返回一个新的链表
    • 即 也是一个逆序的链表

细节点:

  • 两个链表都是非空的
  • 两个非负整数
  • 两个数字都不会以0开头
  • 数字逆序排列,第一个节点表示整数的最低位
  • 题目没有明确整数的范围,这是个容易忽略的点

实现方案

  1. 暴利破解1 - 链表转数字,相加,再转字符数组,输出新链表
  2. 暴利破解2 - 链表转数字,相加,转换链表
  3. 优化解法 - 链表直接遍历,对应位置数字相加,直接返回新链表

方法1 和 方法2 纯属使用笨方法实现功能,完成 示例 中的案例,但 在 leetCode 提交的时候没有跑过测试 > 主要是存在一个问题:若超过语⾔⽀持的数据类型范围,则报错 ,因为:[题目:没有明确整数的范围] 解决办法:BigInteger

实现方案1 :暴利破解1 - 链表转数字,相加,再转字符数组,输出新链表

实现逻辑:

  • l1、l2 执行相同的步骤:

    • 循环遍历链表,将逆序排列的元素,转换成整数
  • 两个整数相加

  • 转换成 String数组

  • 循环遍历 String数组 逆序 输出新的链表

public ListNode addTwoNumbersOne(ListNode l1, ListNode l2) {
    // 把链表转成数字
    long num1 = 0;
    // 位数:0代表个位,1代表十位,以此递增
    int square1 = 0;
    while (l1 != null) {
        num1 += (long) l1.val * Math.pow(10, square1);
        l1 = l1.next;
        square1++;
    }

    long num2 = 0;
    int square2 = 0;
    while (l2 != null) {
        num2 += (long) l2.val * Math.pow(10, square2);
        l2 = l2.next;
        square2++;
    }
    long sumNum = num1 + num2;

    // 如果 和 为 0 ,则直接返回新的链表
    if (sumNum == 0) {
        return new ListNode(0);
    }
    // 转换成 字符串 数组
    String[] strArr = String.valueOf(sumNum).split("");
    ListNode newNode = null;
    // 循环遍历,组成新的逆序链表
    for (String aStr : strArr) {
        if (newNode == null) {
            newNode = new ListNode(Integer.parseInt(aStr));
        } else {
            newNode = new ListNode(Integer.parseInt(aStr), newNode);
        }
    }
    return newNode;
}

实现方案2 : 暴利破解2 - 链表转数字,相加,转换链表

实现逻辑: 对暴利解法进行优化,去除 整数 转 字符串 数组,减少空间复杂度

  1. 循环遍历链表,将逆序排列的元素,转换成整数
  2. 两个整数相加
  3. 使用 数学思维,对数取余,可获取每位的值,创建成新的链表
public ListNode addTwoNumbersTwo(ListNode l1, ListNode l2) {
    long num1 = 0;
    int square1 = 0;
    while (l1 != null) {
        num1 += (long) l1.val * Math.pow(10, square1);
        l1 = l1.next;
        square1++;
    }

    long num2 = 0;
    int square2 = 0;
    while (l2 != null) {
        num2 += (long) l2.val * Math.pow(10, square2);
        l2 = l2.next;
        square2++;
    }
    long sumNum = num1 + num2;

    ListNode head = new ListNode(); // 创建一个新链表,头部为空节点
    ListNode cur = head;
    if (sumNum == 0) {
        return new ListNode(0);
    }
    // 循环遍历,两数之和
    while (sumNum > 0) {
        // 每次 对整数 取余 获取最低位值
        int val = (int) sumNum % 10;
        // 添加到链表中
        cur.next = new ListNode(val);
        cur = cur.next;
        // 移除 整数的 最低位值
        sumNum = sumNum / 10;
    }
    return head.next;
}

实现方案3 : 链表直接遍历,对应位置数字相加,直接返回新链表

实现逻辑:

  • 同时 循环遍历2个链表,对 两个链表同位置的数,进行累加,追加到新链表中
  • 设置 进位数
    • 每位数 相加时,都要加一下进位数,以防有进位
  • 链表 全部遍历完成后,判断是否还有进位,有进在尾部,追加进位数

核心计算公式:sum = x的最后位数 + y的最后位数 + carry进位数

public ListNode addTwoNumbersThree(ListNode l1, ListNode l2) {
    ListNode head = null, tail = null;
    int carry = 0;
    while (l1 != null || l2 != null) {
        // 判断链表 该位是否有值,无值,则返回零
        int x = l1 != null ? l1.val : 0;
        int y = l2 != null ? l2.val : 0;

        // 两链表位数相加 and 加 进位值
        int temp = x + y + carry;

        // 将 累加值 放入新的链表中
        if (head == null) {
            head = tail = new ListNode(temp % 10);
        } else {
            tail.next = new ListNode(temp % 10);
            tail = tail.next;
        }
        // 获取进位值
        carry = temp / 10;
        l1 = l1 == null ? l1 : l1.next;
        l2 = l2 == null ? l2 : l2.next;
    }

    if (carry > 0) {
        tail.next = new ListNode(carry);
    }
    return head;
}

github 地址

案例地址