题目连接:2.两数相加

题意

题目难度标为 中等, 因为题意上有一部分理解难度,以及需要数据结构的链表基础。

还不知道到链表的童鞋可以粗略的看下百度百科或者是翻出数据结构的书看一看,通俗一点的语言来解释链表就是:上线和下线

上线知道自己的下线,但不知道自己下线的下线,同时也不知道自己的上线是谁。

这就是单向链表

这道题的题意就是将两个数字变成了两个单向链表,其中每一个节点存储一位数字,且是逆序存放,也就是倒过来存了。

解题思路

首先来想一下不同情况和对应的案例:

  1. 两个链表长度相等。

输入:(2 -> 4 -> 3) + (5 ->6 -> 4)

输出:(7 -> 0 -> 8)

  1. 两个链表长度不等。

输入:(2 -> 4) + (5 -> 6 -> 4)

输出:(7 -> 0 -> 5)

  1. 最终结果的最高位存在进位的情况。

输入:(2 -> 4 -> 5) + (5 -> 6 -> 4)

输出:(7 -> 0 -> 0 -> 1)

解题的关键便是:合并链表并且保证进位正确

模拟进位

首先我们按位相加,然后再对每一位进行进位处理,满 10 则进 1

代码:

    public class Solution
{
public ListNode AddTwoNumbers(ListNode l1, ListNode l2)
{
ListNode first = null;
ListNode current = null;
IList<int> sums = new List<int>();
int sum = 0;
int res = 0; while (l1 != null || l2 != null)
{
sum = res;
if (l1 != null)
{
sum += l1.val;
l1 = l1.next;
} if (l2 != null)
{
sum += l2.val;
l2 = l2.next;
} res = sum / 10;
sum %= 10;
sums.Add(sum);
} if(res > 0) sums.Add(res); if(sums.Any()) first = new ListNode(sums[0]); current = first; for (int i = 1; i < sums.Count; i++)
{
current.next = new ListNode(sums[i]);
current = current.next;
} return first;
}
}

执行用时: 252 ms, 在所有 C# 提交中击败了13.33%的用户

内存消耗: 26.7 MB

这个耗时有点凄惨,接近垫底了。那也说明了还有很大的优化空间。

优化常量

上面我们在循环时使用到了 IListCount,这里我们可以提前将其存储起来。

代码如下:

    public class Solution
{
public ListNode AddTwoNumbers(ListNode l1, ListNode l2)
{
ListNode first = null;
ListNode current = null;
IList<int> sums = new List<int>();
int sum = 0;
int res = 0;
int count = 0; while (l1 != null || l2 != null)
{
sum = res;
if (l1 != null)
{
sum += l1.val;
l1 = l1.next;
} if (l2 != null)
{
sum += l2.val;
l2 = l2.next;
} res = sum / 10;
sum %= 10;
sums.Add(sum);
} if (res > 0) sums.Add(res); count = sums.Count; if (count > 0) first = new ListNode(sums[0]); current = first; for (int i = 1; i < count; i++)
{
current.next = new ListNode(sums[i]);
current = current.next;
} return first;
}
}

执行用时: 164 ms, 在所有 C# 提交中击败了85.62%的用户

内存消耗: 26.8 MB

仅仅是替换了一个变量,执行用时就优化了近 100ms! 这 100ms 就超过了 70% 的提交。

前面还有近 15% 说明还有优化空间。

优化循环数

上面的算法,如果按照大O表示法来计算复杂度的话,它的复杂度是:O(max(l1.Length, l2.Length)) ,再精简一下也就是 O(n),即单重循环的程度。

但真正的复杂度是(同样也是估算,循环内的操作数没有算进来,这里只算了循环的):2 * max(l1.Length, l2.Length) + 1。因为这里实际上是使用了两个循环。那么我们可以将这个复杂度表达式中的倍数 : 2 去掉,即只用一重循环。

代码如下:

    public class Solution
{
public ListNode AddTwoNumbers(ListNode l1, ListNode l2)
{
ListNode first = null;
ListNode current = null;
int sum = 0;
int res = 0; while (l1 != null || l2 != null)
{
sum = res;
if (l1 != null)
{
sum += l1.val;
l1 = l1.next;
} if (l2 != null)
{
sum += l2.val;
l2 = l2.next;
} res = sum / 10;
sum %= 10; if (current == null)
{
first = new ListNode(sum);
current = first;
}
else
{
current.next = new ListNode(sum);
current = current.next;
}
} if (res > 0) current.next = new ListNode(res); return first;
}
}

执行用时: 136 ms, 在所有 C# 提交中击败了98.85%的用户

内存消耗: 26.5 MB

在我们移除掉一重循环之后,执行用时优化了 20 多ms(为什么不是优化了近一半的时间?),而这 20 多ms就又超过了 13% 的提交。

对于Leetcode判题耗时的思考

处于对连续两道题目都没有办法达到最优(至少前 1% )的情况下,若羽今天写到这里时,特意去看了一下耗时最短的代码(104ms),复制下来之后直接提交,结果显示是 248ms !??

再次提交之后结果显示是 160 ms !??

同一份代码, 上下浮动的区间未免也太大了!若羽不禁思考起 LeetCode 的判题核心是如何进行计时的。这个浮动区间已经达到可以破坏排名公平性的程度了,也许有人会觉得若羽危言耸听,夸大其词。

事实是:确实已经达到破坏公平的程度了!!!,这一类在线运行代码并且自动输入案例比对结果的系统其实很早就已经出现,在 信息学竞赛 以及 ACM大学生程序设计竞赛 中通常被称为 OJ(Online Judge System) 在线判题系统。而在竞赛场上,差距达到 160ms 意味着什么?意味着两队选手同时甚至略迟一点点提交代码,最终谁的排名在前还得听天命

抛开竞赛的立场,浮动如此大的排名,在一定程度上已经无法再客观的去评价时间复杂度相近算法的优劣了。排名第一的算法再运行一次也有可能会排到末尾去。

但是算法优化的思路还是可以继续,撸码不停,优化不止~(好想自己实现一个 OJ 了!)

LeetCode :2.两数相加 解题报告及算法优化思路的更多相关文章

  1. LeetCode :1.两数之和 解题报告及算法优化思路

    最近开始重拾算法,在 LeetCode上刷题.顺便也记录下解题报告以及优化思路. 题目链接:1.两数之和 题意 给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 ...

  2. leetcode之两数相加解题思路

    问题描述 给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标. 你可以假设每种输入只会对应一个答案.但是,数组中同一个元素不能使 ...

  3. Leetcode 002. 两数相加

    1.题目描述 给出两个 非空 的链表用来表示两个非负的整数.其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字. 如果,我们将这两个数相加起来,则会返回一个新的链表 ...

  4. LeetCode 445——两数相加 II

    1. 题目 2. 解答 2.1 方法一 在 LeetCode 206--反转链表 和 LeetCode 2--两数相加 的基础上,先对两个链表进行反转,然后求出和后再进行反转即可. /** * Def ...

  5. Leetcode 445. 两数相加 II

    1.题目描述 给定两个非空链表来代表两个非负整数.数字最高位位于链表开始位置.它们的每个节点只存储单个数字.将这两数相加会返回一个新的链表. 你可以假设除了数字 0 之外,这两个数字都不会以零开头. ...

  6. LeetCode 445. 两数相加 II(Add Two Numbers II)

    445. 两数相加 II 445. Add Two Numbers II 题目描述 给定两个非空链表来代表两个非负整数.数字最高位位于链表开始位置.它们的每个节点只存储单个数字.将这两数相加会返回一个 ...

  7. LeetCode 2. 两数相加(Add Two Numbers)

    2. 两数相加 2. Add Two Numbers 题目描述 You are given two non-empty linked lists representing two non-negati ...

  8. LeetCode 2——两数相加(JAVA)

    给出两个 非空 的链表用来表示两个非负的整数.其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字. 如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和 ...

  9. LeetCode 2. 两数相加(Add Two Numbers)

    题目描述 给定两个非空链表来表示两个非负整数.位数按照逆序方式存储,它们的每个节点只存储单个数字.将两数相加返回一个新的链表. 你可以假设除了数字 0 之外,这两个数字都不会以零开头. 示例: 输入: ...

随机推荐

  1. 机器学习之使用Python完成逻辑回归

    一.任务基础 我们将建立一个逻辑回归模型来预测一个学生是否被大学录取.假设你是一个大学系的管理员,你想根据两次考试的结果来决定每个申请人的录取机会.你有以前的申请人的历史数据,你可以用它作为逻辑回归的 ...

  2. 微服务-springboot热部署

    spring为开发者提供了一个名为spring-boot-devtools的模块来使Spring Boot应用支持热部署,提高开发者的开发效率,无需手动重启Spring Boot应用. IDEA进行热 ...

  3. flutter 如何实现文件读写(使用篇)

    flutter文件读写可以对磁盘文件进行操作,实现某些业务场景,那么我们开始来讲下这个文件读写操作. 使用的库插件(package) dart:io(用于数据处理) path_provider (用于 ...

  4. Jmh测试JDK,CGLIB,JAVASSIST动态代理方式的性能

    前言 JDK,CGLIB,JAVASSIST是常用的动态代理方式. JDK动态代理仅能对具有接口的类进行代理. CGLIB动态代理方式的目标类可以没有接口. Javassist是一个开源的分析.编辑和 ...

  5. ZOJ 3795:Grouping(缩点+最长路)

    http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=5303 题意:有n个人m条边,每条边有一个u,v,代表u的年龄大于等于v,现在要 ...

  6. tomcat问题解决

    tomcat问题解决 运行tomcat环境下,idea中出现 error running 项目名address localhost1099 is already in use 的时候,如何解决? 1, ...

  7. 上传文件不落地转Base64字符串

    1. 问题描述 因需调用第三方公司的图像识别接口,入参是:证件类型.图像类型.图片base64字符串,采用http+json格式调用. 本来采用的方式是:前端对图片做base64处理,后端组装下直接调 ...

  8. 剑指offer第二版-2.实现单例模式

    面试题2:实现单例模式 题目要求: 设计一个类,只能生成该类的一个实例. /** * 单例模式 * 定义:指实现了特殊模式的类,该类仅能被实例化一次,产生唯一的一个对象 * 应用举例:windows的 ...

  9. Hadoop值Partition分区

    分区操作 为什么要分区? 要求将统计结果按照条件输出到不同文件中(分区).比如:将统计结果按 照手机归属地不同省份输出到不同文件中(分区) 默认 partition 分区 /** 源码中:numRed ...

  10. MyBatis 中 @Param 注解的四种使用场景,最后一种经常被人忽略!

    有一些小伙伴觉得 MyBatis 只有方法中存在多个参数的时候,才需要添加 @Param 注解,其实这个理解是不准确的.即使 MyBatis 方法只有一个参数,也可能会用到 @Param 注解. 但是 ...