zoukankan      html  css  js  c++  java
  • Leetcode解题-链表(2.2.1)AddTwoNumbers

    题目:2.2.1 Add Two Numbers

    You are given two linked lists representing two non-negative numbers. The digits are stored in reverse order and each of their nodes contain a single digit. Add the two numbers and return it as a linked list.

    Input: (2 -> 4 -> 3) + (5 -> 6 -> 4)

    Output: 7 -> 0 -> 8

    解决问题

    看起来很简单的一道题目,但正好可以当成实例,学习一下如何将将题目变成最终正确、高效的代码。

    2.1 问题定义

    首先,第一步就是定义问题,确定好问题的域(problem domain)也就成功了一大半。例如,对于题目2.2.1

    Ø  确定输入很关键,输入是两个链表代表两个非负数,编码方式是:每个结点的值一个1位的数字,结点按逆序从个位到最高位排列。

    Ø  输出很简单,就是返回一个链表,代表输入的两个链表的加和。

    有了这些限定,我们就确定了要解决的问题是什么样子,有了方向才能进行下一步,从而不会跑偏。


    2.2 设计算法

    由于这道题目很明确,就是处理链表,所以就剩却了选取和设计数据结构这一步,我们就可以直接设计算法了。问题很简单,也不需要分治啊、动态规划啊什么的高级技术,老老实实的遍历两个链表就能解决问题了。

    Algorithm addTwoNumbers(list1, list2)

           // Input:

           //            list1 and list2 representing 2 non-negative numbers

           //            Each node containing single digit

           //            All digits sorted in reversed order

           // Output:

           //           Result list representing the sum of list1 and list2

           //            in the same way of input list(single digit, reversed order)

           cur1 <- list1, cur2 <- list2

           carry <- 0, sum <- 0

     

           while cur1 != null or cur2 != null

           do

                  if cur1 != null and cur2 != null

                         sum <- cur1 + cur2 + carry

                  else if cur1 != null

                         sum <- cur1 + carry

                  else

                         sum <- cur2 + carry

                 

                  result.addNewNode(sum % 10)

                 

                  carry <- sum / 10

                  sum <- 0

     

                  if cur1 != null do cur1 <- cur1.next

                  if cur2 != null do cur2 <- cur2.next

           end

                 

           if carry > 0

                  result.addNewNode(carry)

     

           return result

    2.3 分析算法

    写好了伪代码,不管是在面试还是自己练习,我们可能都要问:这设计的对吗?效率是多少啊?所以,还不能急着写代码,先证明一下自己的算法确实正确,而且还很高效。本题的代码,或者说几乎所有代码中,不管是证明正确性还是分析效率,循环都是最重要的。所以这里我们就好好探讨一下,如何写好循环:

    1) 初始化:需要哪些变量,变量初始化成什么,是否需要定义在循环外;

    【本例】:需要三个指针,其中cur1cur2遍历输入链表和result遍历结果链表,初始化为三个链表的头结点。此外,每次迭代还需要保存总和sum和进位carry,都初始化为0。这里只有sum是可以放在循环内的。

    2) 终止条件:什么条件下可以继续循环,举个例子看看会不会出现off-by-one问题;

    【本例】:因为只要有一个链表没到尾部,加法过程就要继续,所以循环条件就是cur1cur2至少有一个不是null

    3) 循环体:循环体里应该做什么,有没有break, continue, return等其他改变循环顺序的出口,迭代使用变量是否每次都清0了;

    【本例】:循环体里分三步1)加和;2)将sum的个位数添加到result尾部;3)处理进位,清空sum,准备下一轮迭代。

    4) 计数器:计数器怎样累加,是否能每次迭代都变大或变小从而使循环正常终止,而不会造成死循环;

    【本例】:在循环体最后,cur1cur2不为空时,就向后移动,所以循环最终是能够终止的。因为没有使用for循环,所以这一部分代码也放在循环体里了。

    5) 后处理:跳出循环后,需不需要进行一些遗留处理,使计算结果变完整,例如分页的最后一批数据。

    【本例】:全部累积完毕退出循环后,也要考虑进位问题。

    可以用断言和循环不变量严谨地证明正确性。同时也不难看出,算法的时间复杂度与list1list2规模的较大者相关,较大者的长度决定了循环的次数。

    2.4 实现代码

    终于可以上手写代码了,这一步的重点是如何用好一种编程语言(C++)实现上面的算法。相比伪代码,在实现时我们要额外考虑:如何创建链表(用dummy header方法)、如何插入新结点等具体实现问题。这些问题都是容易出错的,所以不能掉以轻心!


    2.5 层次设计和代码测试

    在真实项目里,项目代码不可能只是一个实现了某个算法的函数这么简单。通过这些类或者函数只是小积木,写完之后我们还要累积成更大的积木。同时,在真实环境里,单元测试也是必不可少的。尽管上面已经证明了伪代码是正确且高效的,但只要不是最严谨的数学证明,代码就可能还是有小bug。而且,我们也免不了犯一些与算法无关,但与具体编程语言和操作系统环境有关错误,例如最低级的“==”比较,例如调用操作系统等外部API没进行异常处理等。所以测试是必不可少的,它能增强我们的信心。但在这里,这两个步骤就都省略了。

  • 相关阅读:
    mysql-master-ha 实现mysql master的高可用。
    一个不错的工具版本管理工具
    java的日志知识
    从解决一个java.lang.NoSuchMethodError想到的
    一个单点登录问题的解决
    关于2013年1月21日的DNS故障分析文章
    每日好的资源整理
    mongodb3.4 sharding安装文档
    python 函数
    codis3安装测试
  • 原文地址:https://www.cnblogs.com/xiaomaohai/p/6157642.html
Copyright © 2011-2022 走看看