LeetCode :2.两数相加 解题报告及算法优化思路
题目连接:2.两数相加
题意
题目难度标为 中等, 因为题意上有一部分理解难度,以及需要数据结构的链表基础。
还不知道到链表的童鞋可以粗略的看下百度百科或者是翻出数据结构的书看一看,通俗一点的语言来解释链表就是:上线和下线。
上线知道自己的下线,但不知道自己下线的下线,同时也不知道自己的上线是谁。
这就是单向链表。
这道题的题意就是将两个数字变成了两个单向链表,其中每一个节点存储一位数字,且是逆序存放,也就是倒过来存了。
解题思路
首先来想一下不同情况和对应的案例:
- 两个链表长度相等。
输入:(2 -> 4 -> 3) + (5 ->6 -> 4)
输出:(7 -> 0 -> 8)
- 两个链表长度不等。
输入:(2 -> 4) + (5 -> 6 -> 4)
输出:(7 -> 0 -> 5)
- 最终结果的最高位存在进位的情况。
输入:(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
这个耗时有点凄惨,接近垫底了。那也说明了还有很大的优化空间。
优化常量
上面我们在循环时使用到了 IList 的 Count,这里我们可以提前将其存储起来。
代码如下:
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.两数相加 解题报告及算法优化思路的更多相关文章
- LeetCode :1.两数之和 解题报告及算法优化思路
最近开始重拾算法,在 LeetCode上刷题.顺便也记录下解题报告以及优化思路. 题目链接:1.两数之和 题意 给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 ...
- leetcode之两数相加解题思路
问题描述 给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标. 你可以假设每种输入只会对应一个答案.但是,数组中同一个元素不能使 ...
- Leetcode 002. 两数相加
1.题目描述 给出两个 非空 的链表用来表示两个非负的整数.其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字. 如果,我们将这两个数相加起来,则会返回一个新的链表 ...
- LeetCode 445——两数相加 II
1. 题目 2. 解答 2.1 方法一 在 LeetCode 206--反转链表 和 LeetCode 2--两数相加 的基础上,先对两个链表进行反转,然后求出和后再进行反转即可. /** * Def ...
- Leetcode 445. 两数相加 II
1.题目描述 给定两个非空链表来代表两个非负整数.数字最高位位于链表开始位置.它们的每个节点只存储单个数字.将这两数相加会返回一个新的链表. 你可以假设除了数字 0 之外,这两个数字都不会以零开头. ...
- LeetCode 445. 两数相加 II(Add Two Numbers II)
445. 两数相加 II 445. Add Two Numbers II 题目描述 给定两个非空链表来代表两个非负整数.数字最高位位于链表开始位置.它们的每个节点只存储单个数字.将这两数相加会返回一个 ...
- LeetCode 2. 两数相加(Add Two Numbers)
2. 两数相加 2. Add Two Numbers 题目描述 You are given two non-empty linked lists representing two non-negati ...
- LeetCode 2——两数相加(JAVA)
给出两个 非空 的链表用来表示两个非负的整数.其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字. 如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和 ...
- LeetCode 2. 两数相加(Add Two Numbers)
题目描述 给定两个非空链表来表示两个非负整数.位数按照逆序方式存储,它们的每个节点只存储单个数字.将两数相加返回一个新的链表. 你可以假设除了数字 0 之外,这两个数字都不会以零开头. 示例: 输入: ...
随机推荐
- 正确写网站title、keywords、description
一.title title,也就是标题,是在浏览器上面显示出来的,方便用户了解这个页面的内容;特别是搜索引擎判断你网页内容的主要根据.搜索引擎就很大部分是依靠网站title来判断你网站是关于什么内容的 ...
- hihoCoder 1308:搜索二·骑士问题(BFS预处理)
题目链接 题意 中文题意. 思路 对于每一个骑士,可以先预处理出到达地图上某个点的需要走的步数,然后最后暴力枚举地图上每一个点,让三个骑士走过的距离之和最小即可. #include <bits/ ...
- 【带着canvas去流浪(11)】Three.js入门学习笔记
目录 一. 资料推荐及建议 二. Three.js中的基本概念 三.重点笔记 四.补充示例 示例代码托管在:http://www.github.com/dashnowords/blogs 博客园地址: ...
- JavaScript权威指南第六版(阅读笔记)
前言: 对于软件行业学习是无止境的,因为知识更替非常快,能够快速稳固掌握一门新技术是一个程序员应该具备的基本素质. 了解一门语言,了解它的概念非常重要,但是一些优秀的设计思想需要细心和大量实践才能慢慢 ...
- python通过TimedRotatingFileHandler按时间切割日志
通过TimedRotatingFileHandler按时间切割日志 线上跑了一个定时脚本,每天生成的日志文件都写在了一个文件中.但是日志信息不可能输出到单一的一个文件中. 原因有二:1.日志文件越来越 ...
- Excel公式中问题-记住不要忽略空格!
总结一下之前犯得愚蠢的小问题: 程序:每日报表:从DB下载数据填充到excel,包括3个sheet,sheet1:总结<模板,公式填充,数据源为sheet2,sheet3>;sheet2: ...
- [记录]Python的master-worker和epoll模式
#master-worker模型: #coding:utf-8 import os import sys import socket import time import traceback impo ...
- POJ2533&&SP1799 The Bottom of a Graph(tarjan+缩点)
POJ2553 SP1799 我们知道单独一个强连通分量中的所有点是满足题目要求的 但如果它连出去到了其他点那里,要么成为新的强连通分量,要么失去原有的符合题目要求的性质 所以只需tarjan缩点求出 ...
- 面向对象和pickle模块结合
面向对象和pickle模块相关 1.面向对象: class 类名: def init(self,参数1,参数2): self.对象的属性1 = 参数1 self.对象的属性2 = 参数2 def 方法 ...
- socket发送请求,协程
1.socket发送请求 #发送请求的方式 #方式一 import requests ret = requests.get("https://www.baidu.com/s?wd=abc&q ...