算法修炼之路——【链表】Leetcode 445 两数相加 II

题目描述

给定两个非空链表来代表两个非负整数。数字最高位位于链表开始位置。它们的每个节点只存储单个数字。将这两数相加会返回一个新链表。

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

示例1:

输入: listA = [7, 2, 4, 3]; listB = [5, 6, 4]
输出: [7, 8, 0, 7]
解释: 3427 + 4650 = 7807

(通过翻转链表后,采用Leetcode 2 方法进行解题的方式,时间复杂度为O(max(m, n))); 空间复杂度为O(max(m, n))

不允许翻转链表的情况下,需要预先得知链表的相对长度。

思路分析

在这里我们仅仅讨论与Leetcode2不同的部分,Leetcode2主要讨论了左对齐(头节点对其,尾部补0)情况下的加法实现;而这道题是右对齐(尾节点对其,头节点补0)情况下,我们直接讨论如何实现尾节点对齐的情况。

我们依旧先考虑一般情况,当两个链表相等时,我们直接可以进行相加,返回哨兵节点的后置节点即可(这里可参考Leetcode2)。我们直接给出核心代码:

代码1:一般规则下,两链表相加

扫描二维码关注公众号,回复: 10972791 查看本文章
ListNode pA = listA;
ListNode pB = listB;
ListNode dummyHead = new ListNode(0);

while((pA != null && pB != null) || carry != 0){
	sum = (pA == null ? 0 : pA.val)
		+ (pB == null ? 0 : pB.val)
		+ carry;
	curr = sum % 10;
	carry = sum / 10;
}

// insert newNode into dummyHead's next
ListNode newNode = new ListNode(curr);
newNode.next = dummyHead.next;
dummyHead.next = newNode;

pA = pA == null ? null : pA.next;
pB = pB == null ? null : pB.next;

接下来我们对边界情况或难点进行进一步梳理:

难点考虑

这里题目已经说明非空链表,故我们不考虑非空链表情况。
当两链表不等时,如何使两链表以右对齐,即尾节点对齐的方式相加?

思考1

思考1:将长链表多出的部分,在短链表处补充ListNode(0)节点
这里详细含义见图1:
在这里插入图片描述
图1
我们通过在较短链表listB链首补全listNode(0)节点,使得两链表长度相等,之后两数相加过程就为代码1,这里直接给出 节点补全代码与两数相加代码(这里我们称此方法为加式解法):

代码2:加式解法核心代码

ListNode pA = listA;
ListNode pB = listB; 
ListNode dummyHead = new ListNode(0);
ListNode assiNodePrev = new ListNode(0);

int carry = 0; // carry bit
int sum;
int curr;

// Step1: equalise length of the two linkedlist
int lenA = 0;
int lenB = 0;

// obtain length of listA and listB 
while (pA != null) {
     lenA++;
     pA = pA.next;
}

while (pB != null) {
     lenB++;
     pB = pB.next;
}

/* Step2: make them two linkedlist has euqal length */
int deltaNodeNum = lenA - lenB;

if (deltaNodeNum > 0) { // len(ListA) > len(ListB)
    assiNodePrev.next = listB;
} else if (deltaNodeNum < 0) {
    assiNodePrev.next = listA;
}

// fill vacany nodes with value of O
for (int i = 0; i < Math.abs(deltaNodeNum); i++) {
    ListNode node = new ListNode(0);
    node.next = assiNodePrev.next;
    assiNodePrev.next = node;
}

/* upgrate orientation of pA and pB */
pA = deltaNodeNum < 0 ? assiNodePrev.next : listA;
pB = deltaNodeNum > 0 ? assiNodePrev.next : listB;

/* Step3: add values corrsponding nodes
and
insert active node into <outputList>
 */
while ((pA != null && pB != null) || carry != 0) {
     sum = (pA == null ? 0 : pA.val)
            + (pB == null ? 0 : pB.val)
            + carry;
     curr = sum % 10;
     carry = sum / 10;

     // insert newNode into dummyHead's behind
	 ListNode newNode = new ListNode(curr);
     newNode.next = dummyHead.next;
     dummyHead.next = newNode;

     pA = pA == null ? null : pA.next;
     pB = pB == null ? null : pB.next;
}

思考2

思考2:将长链表多出的部分,直接倒序插入哨兵节点后
在这里插入图片描述
思考2中,我们直接将参差不齐的部分插入输出节点,这样会减少一部分计算量以及内内存的消耗,尤其在两链表长度相差较大时性能提升明显。这里相较于思考1的加式解法,我们相当于将多出的“尾巴部分”剪掉了,故这里称为**“减式解法”**,直接给出核心代码有:

代码3:减式解法

/* Step1:
Init. pointers and integers
*/
ListNode pA = listA;// 
ListNode pB = listB;// 
ListNode dummyHead = new ListNode(0);
ListNode assiNodePrev = new ListNode(0);

int carry = 0; // carry bit
int sum;
int curr;
        
int lenA = 0;
int lenB = 0;

/* Step2: obtain different section between listA and listB
and 
upgrate pA and pB
*/
        
while (pA != null) {
   lenA++;
   pA = pA.next;
}

while (pB != null) {
    lenB++;
    pB = pB.next;
}

// check which is longer one 
int deltaNodeNum = lenA - lenB;

if (deltaNodeNum > 0) { // len(ListA) > len(ListB)
    assiNodePrev.next = listA;
} else if (deltaNodeNum < 0) {
    assiNodePrev.next = listB;
}

// insert nodes which are section longer than other linkelist
// into dummyHead-linkedlist
for (int i = 0; i < Math.abs(deltaNodeNum); i++) {

   ListNode node = new ListNode(assiNodePrev.next.val);
   node.next = dummyHead.next;
   dummyHead.next = node;

   assiNodePrev = assiNodePrev.next;
}

// upgrate orientation of pA and pB 
pA = deltaNodeNum > 0 ? assiNodePrev.next : listA;
pB = deltaNodeNum < 0 ? assiNodePrev.next : listB;

        
/* Step3: add values corrsponding nodes
and
insert active node into <outputList>
*/
while ((pA != null && pB != null) || carry != 0) {
     sum = (pA == null ? 0 : pA.val)
            + (pB == null ? 0 : pB.val)
            + carry;
     curr = sum % 10;
     carry = sum / 10;

     // insert newNode into dummyHead's behind
	 ListNode newNode = new ListNode(curr);
     newNode.next = dummyHead.next;
     dummyHead.next = newNode;

     pA = pA == null ? null : pA.next;
     pB = pB == null ? null : pB.next;
}

解题代码

    public static ListNode solutionWithAdd(ListNode listA, ListNode listB) {

        /* Step1:
        Init. pointers and integers
         */
        ListNode pA = listA;
        ListNode pB = listB; 
        ListNode dummyHead = new ListNode(0);
        ListNode assiNodePrev = new ListNode(0);

        int carry = 0; // carry bit
        int sum;
        int curr;

        // Step1: equalise length of the two linkedlist
        int lenA = 0;
        int lenB = 0;

        // obtain length of listA and listB 
        while (pA != null) {
            lenA++;
            pA = pA.next;
        }

        while (pB != null) {
            lenB++;
            pB = pB.next;
        }

        /* Step2: make them two linkedlist has euqal length */
        int deltaNodeNum = lenA - lenB;

        if (deltaNodeNum > 0) { // len(ListA) > len(ListB)
            assiNodePrev.next = listB;
        } else if (deltaNodeNum < 0) {
            assiNodePrev.next = listA;
        }

        // fill vacany nodes with value of O
        for (int i = 0; i < Math.abs(deltaNodeNum); i++) {
            ListNode node = new ListNode(0);
            node.next = assiNodePrev.next;
            assiNodePrev.next = node;
        }

        // upgrate orientation of pA and pB 
        pA = deltaNodeNum < 0 ? assiNodePrev.next : listA;
        pB = deltaNodeNum > 0 ? assiNodePrev.next : listB;

        /* Step3: add values corrsponding nodes
        and
        insert active node into <outputList>
         */
        while ((pA != null && pB != null) || carry != 0) {
            sum = (pA == null ? 0 : pA.val)
                    + (pB == null ? 0 : pB.val)
                    + carry;
            curr = sum % 10;
            carry = sum / 10;

            // insert newNode into dummyHead's behind
            ListNode newNode = new ListNode(curr);
            newNode.next = dummyHead.next;
            dummyHead.next = newNode;

            pA = pA == null ? null : pA.next;
            pB = pB == null ? null : pB.next;
        }

        return dummyHead.next;
    }


    public static ListNode solutionWithMinus(ListNode listA, ListNode listB) {
        
        /* Step1:
        Init. pointers and integers
         */
        ListNode pA = listA;// 
        ListNode pB = listB;// 
        ListNode dummyHead = new ListNode(0);
        ListNode assiNodePrev = new ListNode(0);

        int carry = 0; // carry bit
        int sum;
        int curr;
        
        
        int lenA = 0;
        int lenB = 0;

        /* Step2: obtain different section between listA and listB
        and 
        upgrate pA and pB
        */
        
        while (pA != null) {
            lenA++;
            pA = pA.next;
        }

        while (pB != null) {
            lenB++;
            pB = pB.next;
        }

        // check which is longer one 
        int deltaNodeNum = lenA - lenB;

        if (deltaNodeNum > 0) { // len(ListA) > len(ListB)
            assiNodePrev.next = listA;
        } else if (deltaNodeNum < 0) {
            assiNodePrev.next = listB;
        }

        // insert nodes which are section longer than other linkelist
        // into dummyHead-linkedlist
        for (int i = 0; i < Math.abs(deltaNodeNum); i++) {

            ListNode node = new ListNode(assiNodePrev.next.val);
            node.next = dummyHead.next;
            dummyHead.next = node;

            assiNodePrev = assiNodePrev.next;
        }

        // upgrate orientation of pA and pB 
        pA = deltaNodeNum > 0 ? assiNodePrev.next : listA;
        pB = deltaNodeNum < 0 ? assiNodePrev.next : listB;

        
        /* Step3: add values corrsponding nodes
        and
        insert active node into <outputList>
         */
        while ((pA != null && pB != null) || carry != 0) {
            sum = (pA == null ? 0 : pA.val)
                    + (pB == null ? 0 : pB.val)
                    + carry;
            curr = sum % 10;
            carry = sum / 10;

            // insert newNode into dummyHead's behind
            ListNode newNode = new ListNode(curr);
            newNode.next = dummyHead.next;
            dummyHead.next = newNode;

            pA = pA == null ? null : pA.next;
            pB = pB == null ? null : pB.next;
        }

        return dummyHead.next;
    }

复杂度分析

不妨设两链表的长度分别为m, n:

  1. 加式解法
    时间复杂度:我们对数据进行了至少一次的遍历,m + n + 2max(m, n), 这里时间复杂度为O(max(m, n));
    空间复杂度:这里我们需要新建ListNode(0)节点,且需要新的输出链表,故为abs(m - n) + max(m, n), 可知为O(max(m, n))
  2. 减式解法
    时间复杂度:我们对数据进行了至少一次遍历,但对于较短链表并没有额外遍历,m + n + 2min(m, n), 这里时间复杂度为O(max(m, n));
    空间复杂度:这里我们不需要新建ListNode(0)节点,但依旧需要新的输出链表,故为max(m, n), 可知为O(max(m, n))

GitHub源码

完整可运行文件请访问GitHub

发布了47 篇原创文章 · 获赞 55 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/u011106767/article/details/105464071
今日推荐