两数相加

题目

  • 给出两个非空的链表用来表示两个非负整数。其中,他们各自的位数是按照逆序的方式进行存储的,并且他们的每个节点只能存储一位数字。
  • 如果,我们将这两个数字加起来,则会返回一个新的链表来表示他们的和。
  • 可以假设除了数字0以外,这两个数都不会以0开头

示例

输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807

解析

  • 利用直接遍历的方式计算,参考了leetcode中的解析
  • 这里我们自己实现了一种解析,是用Map来实现的,key是位数,value是两者相加的和,比较繁琐,不推荐,但是是自己当时想到的。
方法一:

在这里插入图片描述

  • 这道题其实就是两个数字进行相加求和,我们从最低位开始相加,也就是我们l1和l2的表头开始相加,因为他们是逆序排序的。由于每个数字都应该在0-9之间,所以我们计算两个数字的时候可能会出现“溢出”。例如:5 + 7 = 12。这种情况下,我们呢应该将当前位的值设置成2,并且将进位carry = 1代入到下一次的迭代。进位carry必定是0或者1,这是因为两个数字相加(考虑到进位)可能出现最大的和是9 + 9 + 1 = 19
  • 伪代码如下
  1. 将当前节点初始化为返回列表的哑结点
  2. 将进位carry初始化为0
  3. 将p和q分别初始化为列表l1和l2的头部
  4. 遍历列表l1和l2直至到达他们的尾端
    (1)将x设置为节点p的值。如果p已经达到了l1的尾端,则将其设置为0
    (2)将y设置为节点q的值。如果q已经到达了l2的尾端,则将其设置为0
    (3)设定sum = x + y + carry
    (4)更新进位的值,carry = sum/ 10
    (5)创建一个数值是(sum % 10)的新节点,并将其设置为当前节点的下一个节点,然后将当前节点前进到下一个节点上。
    (6)同时,将p和q前进到下一个节点
  5. 检查carry = 1是否成立,如果成立,则向返回列表追加一个含有数字1的新节点
  6. 返回哑结点的下一个节点。

在这里插入图片描述

代码

	class ListNode {
	    int val;
	    ListNode next;
	    ListNode(int x) {
	        val = x;
	    }
	}
    /**
     * 第二种优化的方法,直接遍历,不再使用map进行中间环节的保存
     * @param l1
     * @param l2
     * @return
     */
    public static ListNode addTwoNumbers2(ListNode l1, ListNode l2) {
        // 最终要返回的节点
        ListNode dummyHead = new ListNode(0);
        ListNode p = l1;
        ListNode q = l2;
        ListNode curr = dummyHead;
        // 进位
        int curry = 0;
        while (p != null || q != null) {
            // 如果l1或者l2不为空
            // 如果当前l1的节点不为空,则取节点的值,否则就用0代替
            int x = p != null ? p.val : 0;
            // 如果当前l2的节点不为空,则取节点的值,否则就用0代替
            int y = q != null ? q.val : 0;
            // 计算总和,记得这里一定要加一个进位
            int sum = x + y + curry;
            // 判断当前的总和是否有进位
            curry = sum / 10;
            // 给结果节点中赋值
            curr.next = new ListNode(sum % 10);
            curr = curr.next;
            if (p != null) {
                p = p.next;
            }
            if (q != null) {
                q = q.next;
            }
        }
        if (curry > 0) {
            // 表示有进位,需要新建一个节点
            curr.next = new ListNode(curry);
        }
        return dummyHead.next;
    }

复杂度分析
  • 时间复杂度:O(max(m,n)),假设m和n分别表示l1和l2的长度,上面的算法最多重复max(m, n)次
  • 空间复杂度:O(max(m,n)),新列表的长度最多就是max(m,n) + 1
方法二
  • 这个方法就是我自己随便写出来的,不推荐使用
	class ListNode {
	    int val;
	    ListNode next;
	    ListNode(int x) {
	        val = x;
	    }
	}
    /**
     * 这个解法的思路是:
     * 1. 将传进来的l1和l2转换成map,key是当前第几位 value是当前位数上的值
     * 2. 找出l1和l2最大的值,遍历
     * 3. 依次取出两个map中对应的位数,若map不足,则取0替代
     * 4. 二者相加,并且加上上一次计算的进位
     * 5. 若遍历结束后,进位是1,则表示新增加一位,则新增一个节点。
     * 6. 将最终map的结果放到ListNode中返回。
     * @param l1
     * @param l2
     * @return
     */
    public static ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        // 当前l1的下标(l1的第几个节点)
        int index1 = 1;
        // 当前l2的下标(l2的第几个节点)
        int index2 = 1;
        // 用来保存l1遍历的结果,key是当前第几个节点,value是当前的值即节点的val值
        Map<Integer, Integer> map1 = new HashMap<>();
        // 用来保存l2遍历的结果,key是当前第几个节点,value是当前的值即节点的val值
        Map<Integer, Integer> map2 = new HashMap<>();
        // 用来保存最终相加完成的结果,key是当前第几个节点,value是当前的值即节点的val值
        Map<Integer, Integer> result = new HashMap<>();
        // 开始遍历l1 对其map1进行赋值
        while (l1 != null) {
            map1.put(index1, l1.val);
            index1 ++;
            l1 = l1.next;
        }
        // 开始遍历l2,对其map2进行赋值
        while (l2 != null) {
            map2.put(index2, l2.val);
            index2 ++;
            l2 = l2.next;
        }
        // 用来保存是否有进位
        int up = 0;
        if (index1 > index2) {
            // 当参数l1的长度大于l2的时候,遍历l1
            for (int i = 1; i < index1; i++) {
                Integer value1 = map1.get(i);
                // 位数不足则用0替代
                Integer value2 = map2.getOrDefault(i, 0);
                // 记得加的时候加上进位
                int value = value1 + value2 + up;
                result.put(i, value % 10);
                // 若除以10不是0则表示有进位
                if (value / 10 != 0) {
                    up = 1;
                } else {
                    up = 0;
                }
            }
        } else {
            // 参考上面的注解
            for (int i = 1; i < index2; i++) {
                Integer value1 = map1.getOrDefault(i, 0);
                Integer value2 = map2.get(i);
                int value = value1 + value2 + up;
                result.put(i, value % 10);
                if (value / 10 != 0) {
                    up = 1;
                } else {
                    up = 0;
                }
            }
        }
        // 如果当前注解不为0 表示有一个进位,应该新增一个节点
        if (up == 1) {
            result.put(Math.max(index1, index2), up);
        }
        // 下面的操作是将result最终结果的map转为ListNode
        ListNode root = new ListNode(0);
        ListNode tmp = root;
        for (int i = 1; i < Math.max(index1, index2); i++) {
            ListNode listNode = new ListNode(result.get(i));
            tmp.next = listNode;
            tmp = listNode;
        }
        // 如果最高位有进位,新增一个节点
        if (result.getOrDefault(Math.max(index1, index2), 0) != 0) {
            ListNode listNode = new ListNode(result.get(Math.max(index1, index2)));
            tmp.next = listNode;
        }
        return root.next;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值