两链表数相加[翻转链表 + 头插法]

前言

这个题目稍显简单,但是也可以作为练习翻转链表和头插法的基础。除此之外,发现三目运算符深受CPU喜爱,但同时也存在全部运算导致改变的缺点。

一、两数相加

在这里插入图片描述

二、翻转链表+头插

package everyday;

// 两数相加。
public class AddTwoNumbers {
    /*
    target:两个用链表代表的数相加。
    类似与大数(数组形式)之和,但数组形式可以随机读取,读前读后,但单向链表就只能从链头开始读。
    所以考察知识点为:
    1-链表翻转:for | 递归都可
    2-大数相加
     */
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        // 翻转链表,for | recursion。这个题好像不用先翻转,太拉跨了。
        // l1 = reverseListForV1(l1);
        // l2 = reverseListRecursionV2(l2);
        // 大数相加。dummyHead + 头插翻转 + 又翻转回来,    纯属练习头插法。
        return reverseListForV1(twoListAdd(l1, l2));
    }

    /**
     * 1-两数链表相加,2-返回头节点。
     *
     * @param l1 一个链表。
     * @param l2 另一个被加的链表。
     * @return 相加之后的新链表头节点。
     */
    private ListNode twoListAdd(ListNode l1, ListNode l2) {
        int plus = 0;// 进位多少。
        ListNode dummyHead = new ListNode();// 统一头与非头节点的操作差异。

        // 两者对应相加
        while (l1 != null && l2 != null) {
            int val = l1.val + l2.val + plus;
            // 采用头插法,防止再次翻转。
            ListNode next = dummyHead.next;
            ListNode node = new ListNode(val % 10);
            dummyHead.next = node;
            node.next = next;
            // 更新进位数。
            plus = val / 10;
            // 更新下一个相加节点,保持循环。
            l1 = l1.next;
            l2 = l2.next;
        }
        // 谁不为空,把剩下的和plus一起运算。
        // bug1:三目运算是会把分支都运算了,不像if那么选择,所以才更得CPU的喜爱。
        // plus = l1 != null ? singleListAdd(dummyHead, l1, plus) : singleListAdd(dummyHead, l2, plus);
        if (l1 != null) plus = singleListAdd(dummyHead, l1, plus);
        if (l2 != null) plus = singleListAdd(dummyHead, l2, plus);
        // 如果最终的plus不为0,给plus也生成一个节点。
        // 同bug1:三目运算是会把分支都运算了,不像if那么选择,所以才更得CPU的喜爱。
        // return 0 != plus ? (dummyHead.next = new ListNode(plus)).next : dummyHead.next;
        if (0 != plus) {
            ListNode next = dummyHead.next, node = new ListNode(plus);
            dummyHead.next = node;
            node.next = next;
        }
        // 返回最终的头节点。
        return dummyHead.next;
    }

    /**
     * 当个链表和进位数进行相机,返回最后的进位数。
     *
     * @param dummy
     * @param l
     * @param plus
     * @return
     */
    private int singleListAdd(ListNode dummy, ListNode l, int plus) {
        while (l != null) {
            int val = l.val + plus;
            // 头插。
            ListNode next = dummy.next;
            ListNode node = new ListNode(val % 10);
            dummy.next = node;
            node.next = next;

            // 更新plus.
            plus = val / 10;
            // 更新下一个操作节点,保持循环。
            l = l.next;
        }
        // 返回最后的进位数。
        return plus;
    }

    /**
     * 1-翻转链表,2-并返回新的头节点。
     *
     * @param l 需要翻转的链表。
     * @return 翻转之后的新头节点。
     */
    private ListNode reverseListRecursionV2(ListNode l) {
        // 当l本身就是null时,结束调用。
        if (l == null) return null;
        // 当递归到最后一个时,这条链表路径走完了,开始回溯进行修改next。
        if (l.next == null) return l;
        // 利用函数栈记录这条有序路径。
        ListNode head = reverseListRecursionV2(l.next);
        // 修改next.
        l.next.next = l;
        l.next = null; // 只为防止第一个节点不会被覆盖,所以赋值为null。
        // 一如既往的返回头节点。
        return head;
    }

    /**
     * 1-翻转链表,2-并返回新的头指针。(for 循环版)
     *
     * @param l 需要被翻转的链表。
     * @return 新的头指针。
     */
    private ListNode reverseListForV1(ListNode l) {
        ListNode pre = null;
        while (l != null) {
            // 记录下一个要修改的节点,保证断链时,还能拿到它。
            ListNode next = l.next;
            // 改变当前节点next指向,指到前一个节点。
            l.next = pre;
            // 更新pre = l;更新l = next,保证继续循环。
            pre = l;
            l = next;
        }
        // 最后遍历到null才结束,所以返回null的前向节点即可。
        return pre;
    }


    // 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;
        }
    }

}

总结

1)翻转链表+头插。
2)三目运算符。

参考文献

[1] LeetCode 两数相加


版权声明:本文为qq_43164662原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。