前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【刷题计划】两数相加

【刷题计划】两数相加

作者头像
你呀不牛
发布2021-11-19 14:32:43
2580
发布2021-11-19 14:32:43
举报
文章被收录于专栏:我要变牛

1刷题两天小结

很多题目还是直接没有思路,如果只是暴力破解又没有什么作用,有的题目思考很长时间也是做不出来,

刷题顺序也没有什么规律,看到拿到刷哪个,搜了下资料,刷题比较少的可以最开始从头开始刷,目前先按照这个规律刷150题左右

1、建议未刷过题的新人按着顺序来。前 150 题覆盖了很多经典题目和知识点,指针法类如『3 sum』系列,动规类如『regex matching』,搜索类题目如『Sodoku Solver』。 2、基本熟悉知识点(图、树、堆、栈、链表、哈希表、记忆搜索、动态规划、指针法、并查集等)后,可以一类类标签强攻。Leetcode 右侧的标签系统虽然未必 100% 完整,但是大致分类做得还不错。 3、面试前的一个月可以只做『Hard』标签的题目,因为一般两遍之后对于大部分『Medium』难度以下的题目都是肌肉记忆了。多练习『Hard』类题目可以让自己的思路更开阔,因为很多题目使用的奇淫巧技让人惊讶,比如 Leetcode 精心设计连续题号的『84. Largest Rectangle in Histogram』、『85. Maximal Rectangle』。

2今日题目

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

请你将两个数相加,并以相同形式返回一个表示和的链表。

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

示例 1:

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

示例 2:

输入:l1 = [0], l2 = [0] 输出:[0]

示例 3:

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

思路

链表问题一般都会使用虚拟表头来控制循环,类似Java的AQS中哨兵节点,都是为了方便递归或者迭代

这道题目也是这个思路

  1. 根据最长的链表来控制整体迭代次数
  2. 添加虚拟节点用于后面建立链表
  3. 相同index下数字之和,如果某个链表没有这个index,默认给0,不会影响求和,如果是求乘机,默认给1
  4. 当前链表节点的值为上个节点之和的进位与当前index的两个节点之和,然后再取模,商用来进位
  5. 当两个链表都迭代完成,如果商数还是大于0, 说明两个链表最后的节点之和大于10,需要继续进位,所以需要多创建一个节点
代码语言:javascript
复制
public static ListNode addTwoNumbers(ListNode l1, ListNode l2) {
    //冗余节点,为了循环方便
    ListNode preNode = new ListNode(-1);
    ListNode currentNode = preNode;
    int shang = 0;
    //长度以最长的链表为主
    while (l1 != null || l2 != null) {
        int sum = getSum(l1, l2);
        //创建下一个节点
        currentNode.next = new ListNode((sum + shang) % 10);
        shang = (sum + shang) / 10;
        //当前节点指向下一个节点
        currentNode = currentNode.next;
        //两个链表分别往后迭代
        l1 = (l1 == null) ? null : l1.next;
        l2 = (l2 == null) ? null : l2.next;
    }
    //链表长度内加完之后,最后余数还是大于0,说明扩展位数
    if (yushu > 0) {
        currentNode.next = new ListNode(yushu);
    }
    return preNode.next;
}

/**
 * 节点为null时按照值为0处理
 */
private static int getSum(ListNode l1, ListNode l2) {
    int val1 = l1 == null ? 0 : l1.val;
    int val2 = l2 == null ? 0 : l2.val;
    return val1 + val2;
}

执行用时:2 ms, 在所有 Java 提交中击败了94.18%的用户 内存消耗:38.6 MB, 在所有 Java 提交中击败了61.42%的用户

这道题加上调试,差不多也用了一个多小时,终于AC后感觉还是不错的,

然后再查看下官网的题解

代码语言:javascript
复制
class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        ListNode pre = new ListNode(0);
        ListNode cur = pre;
        int carry = 0;
        while(l1 != null || l2 != null) {
            int x = l1 == null ? 0 : l1.val;
            int y = l2 == null ? 0 : l2.val;
            int sum = x + y + carry;
            
            carry = sum / 10;
            sum = sum % 10;
            cur.next = new ListNode(sum);

            cur = cur.next;
            if(l1 != null)
                l1 = l1.next;
            if(l2 != null)
                l2 = l2.next;
        }
        if(carry == 1) {
            cur.next = new ListNode(carry);
        }
        return pre.next;
    }
}

执行用时:2 ms, 在所有 Java 提交中击败了94.18%的用户 内存消耗:38.7 MB, 在所有 Java 提交中击败了42.04%的用户

时间和内存几乎一样, 其实整体思路差不多,官网给出了虚拟节点的作用定义:

小技巧:对于链表问题,返回结果为头结点时,通常需要先初始化一个预先指针 pre,该指针的下一个节点指向真正的头结点head。使用预先指针的目的在于链表初始化时无可用节点值,而且链表构造过程需要指针移动,进而会导致头指针丢失,无法返回结果。

同时在其他评论发现了另一个优化点

如果把 carry = sum / 10; 换成 carry = sum >9 ? 1 : 0 ; 这样 速度会快很多

结果:

执行用时:1 ms, 在所有 Java 提交中击败了100.00%的用户 内存消耗:38.5 MB, 在所有 Java 提交中击败了75.16%的用户

这里主要是因为除法运算相对比较耗时的原因,同时题目本身都是整数可以这么特殊处理

类似的优化手段还有位运算替代乘除法等

又是摸鱼+刷题的一天。开心

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-11-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 你呀不牛 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1刷题两天小结
  • 2今日题目
    • 示例 1:
      • 示例 2:
        • 示例 3:
          • 思路
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档