练习题

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

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

解题思路:在任一个链表只剩一个数或者两个链表都只剩一个数的时候,就单独来计算,判断是否产生进位。如果两个链表不等长,还需要再把较长的链表的后几位计算,判断是否产生进位。同样是计算到最后一位,再判断是否产生进位,是否需要链表扩容。

/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode cur1 = l1;
ListNode cur2 = l2;
int count = 0;//每位之和
int s = 0;//进位

while(cur1.next != null && cur2.next != null){//加到其中有一个链表剩一位,或者两个链表都只剩一位
count = cur1.val + cur2.val + s;
if(count > 9){
cur1.val = count % 10 ;
cur2.val = count % 10 ;
s = 1;
}else{
cur1.val = count;
cur2.val = count;
s = 0;
}
cur1 = cur1.next;
cur2 = cur2.next;

}

if(cur1.next == null && cur2.next == null){ //当两个链表都只剩一位时
count = cur1.val + cur2.val +s;
if(count > 9)
{
cur1.val = count % 10;
cur2.val = count % 10;
s = 1;
}else{
cur1.val = count ;
cur2.val = count ;
s = 0;
}
if(s == 1){ //说明最后两个数产生了进位,需要扩充链表。
ListNode tmp = new ListNode(1);
cur1.next = tmp;
return l1;
}
return l1;
}

if(cur1.next == null){ //cur1链表剩一位
count = cur1.val + cur2.val + s ;
cur2.val = cur1.val + cur2.val;
if(count < 10){ //cur1的最后一位加cur2的一位 没产生进位,直接返回
cur2.val = count;
return l2;
}
while(cur2.next != null){ //因为有进位,需要再把cur2的剩余数计算 直到最后一位
count = cur2.val + s;
if(count > 9){
cur2.val = count % 10 ;
s = 1;
} else{
cur2.val = count;
s = 0;
}
cur2 = cur2.next;
}
if(cur2.val + s > 9){ //判断cur2 最后一位是否产生进位
cur2.val = (cur2.val + s) % 10;
s = 1;
}else{
cur2.val = cur2.val +s;
s = 0;
}
if(s == 1){ //产生进位,扩充链表
ListNode tmp = new ListNode(1);
cur2.next = tmp;
return l2;
}
return l2;
}else{ //cur2链表剩一位
count = cur1.val + cur2.val + s;
cur1.val = cur2.val + cur1.val;
if(count < 10){ //cur2的最后一位加cur1的一位 没产生进位,直接返回
cur1.val = count;
return l1;
}
while(cur1.next != null){ //因为有进位,需要再把cur1的剩余数计算 直到最后一位
count = cur1.val + s;
if(count > 9){
cur1.val = count % 10 ;
s = 1;
} else{
cur1.val = count;
s = 0;
}
cur1 = cur1.next;
}
if(cur1.val + s > 9){ //判断cur1 最后一位是否产生进位
cur1.val = (cur1.val + s) % 10;
s = 1;
}else{
cur1.val = cur1.val +s;
s = 0;
}
if(s == 1){ //产生进位,扩充链表
ListNode tmp = new ListNode(1);
cur1.next = tmp;
return l1;
}
}
return l1;
}
}
————————————————

思路 在一个循环里 两个链表同时往下走
数据每位相加 可能有进位
加完新的链表也往下走

假设两个链表分别是:
[4,7,6] [9,1,8]

 

     674 + 819

     假如l1 = (4,7,6) l2 = (9,1,8)

         l1      =      4      7      6
         l2      =      9      1      8
        sum      =     13      8     14            和
       curr      =      3      8      4            当前位
  curr.next      =      1      0      1            进位 进位数值只可能为0或1
       sumX      =      3      9      4      1     进位加在后面 新数



和一般计算略有不同的是,需要把进位往右移。仅此而已

func addTwoNumbers(_ l1: ListNode?, _ l2: ListNode?) -> ListNode? {
    //把当前节点初始化为哑节点
    let dummyHead = ListNode(0)
    //把p初始化为l1的头节点
    var p: ListNode? = l1
    //把q初始化为l2的头节点
    var q: ListNode? = l2
    //把起始节点(最后一个节点)初始化为哑节点
    //思路 每次curr要去获取下一个节点,但是上一个节点的值需要被保存起来
    //dummyHead是用来存储curr的 因为要操作curr去获取下一位 所以上一位必须被保存下来
    var curr: ListNode = dummyHead
    //进位默认为0
    var carry: Int = 0
    //只要有一个链表不为nil就进入循环
    while p != nil || q != nil {
        //如果l1的当前节点不为nil 取当前节点 如果为nil取0
        let x = (p != nil) ? p!.val : 0
        //如果l2的当前节点不为nil 取当前节点 如果为nil取0
        let y = (q != nil) ? q!.val : 0
        //求和
        //当前节点的数值相加并加上进位
        //(第一组数相加完才会有进位,所以第一组数的进位一定为0)
        let sum = carry + x + y
        //数字为一位还是两位 如果是一位 没有进位 如果是两位 有进位
        //一共10个数0->9
        carry = sum / 10
        //新链表当前节点的值为两个原链表当前节点的和除以10(进位)取余
        //(取后一位,因为可能有进位)
        curr.next = ListNode(sum % 10)
        //把下一个节点设置为当前节点
        curr = curr.next!
        //如果l1不为空 往下走查看下一个节点
        if p != nil { p = p!.next }
        //如果l2不为空 往下走查看下一个节点
        if q != nil { q = q!.next }
    }
    //如果carry不为0 则有进位(carry == 1) 把carry带入 直接把最右侧一个节点数值设置为1
    if carry > 0 {
        //curr是当前位 在循环结束后也是最后一位(不含进位)
        curr.next = ListNode(carry)
    }
    //返回链表
    return dummyHead.next;
}





1)方法实现1:
原理如下:原理:第一次给node挂上nextnode的时候,原理是当node开辟出一块内存的时候 temp也指向了node对应的这块内存,给temp的nextcode挂上节点以后,同时也就给node挂上了nextnode,第二次第三次给node挂节点的时候,再把temp指向temp的nextcode
————————————————

public static void main(String[] args) {
ChainNode node1 = new ChainNode(2);
ChainNode node1_1 = new ChainNode(4);
ChainNode node1_2 = new ChainNode(3);
node1.setNextNode(node1_1);
node1_1.setNextNode(node1_2);
ChainNode node2 = new ChainNode(5);
ChainNode node2_1 = new ChainNode(6);
ChainNode node2_2 = new ChainNode(4);
node2.setNextNode(node2_1);
node2_1.setNextNode(node2_2);
ChainNode node = TestAddTwoNumber.getNode(node1,node2);
while(null != node){
System.out.println(node.getVal());
node = node.getNextNode();
}

}

/***
* 给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。
如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。
您可以假设除了数字 0 之外,这两个数都不会以 0 开头。
示例:
输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807

原理:第一次给node挂上nextnode的时候,原理是当node开辟出一块内存的时候 temp也指向了node对应的这块内存,给temp的nextcode挂上节点以后,同时也就给node挂上了nextnode,
第二次第三次给node挂节点的时候,再把temp指向temp的nextcode
为了方便大家理解,我画了个原理图如下:
![在这里插入图片描述](https://www.icode9.com/i/ll/?i=20190408175525581.png?,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl8zOTM2NDI4OQ==,size_16,color_FFFFFF,t_70)
* @param args
*/
public static ChainNode getNode(ChainNode node1,ChainNode node2){
ChainNode node = new ChainNode(0);
ChainNode temp = node;
// ChainNode nodetemp = new ChainNode(0);
int carry = 0;//0 不进位 1 进位
while(node1 != null || node2 != null){
int x = node1.getVal();
int y = node2.getVal();
int sum = x + y + carry;
carry = sum / 10;
temp.setNextNode(new ChainNode(sum % 10));
temp = temp.getNextNode();
node1 = node1.getNextNode();
node2 = node2.getNextNode();
}
// node.setNextNode(nodetemp);
return node.getNextNode();
}
————————————————

上一篇:[Acwing]1165. 单词环 spfa判断负环+分数规划


下一篇:nginx配置多个静态页面不能用root