7

415. 求两数之和

 3 years ago
source link: https://sexywp.com/415-add-two-number.htm
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

415. 求两数之和

Charles 2020/08/170

这个题目其实可以说是很多算法比赛训练的入门题目了,我一早就知道。

给定两个字符串形式的非负整数 num1 和num2 ,计算它们的和。

num1 和num2 的长度都小于 5100

num1 和num2 都只包含数字 0-9

num1 和num2 都不包含任何前导零

你不能使用任何內建 BigInteger 库, 也不能直接将输入的字符串转换为整数形式

引用自 LeetCode 的题库

甚至我们公司也有出过这个题目作为笔试题,但是,昨天我自己做了一遍,发现做得非常差,虽然还是做出来了,但是,写得极其啰嗦。我列在下面:

class Solution:
def addStrings(self, num1: str, num2: str) -> str:
l1 = len(num1)
l2 = len(num2)
if l1 > l2 :
num1, num2 = num2, num1
l1, l2 = l2 , l1
shift = 0
newstr = ''
while i > 0:
lowbit1 = ord(num1[l1 - (l1 - i + 1)]) - 48
lowbit2 = ord(num2[l2 - (l1 - i + 1)]) - 48
lowbit3 = lowbit1 + lowbit2 + shift
if lowbit3 > 9:
lowbit3 = lowbit3 - 10
shift = 1
else:
shift = 0
newstr = str(lowbit3) + newstr
i = i - 1
left = l2 - l1
if left > 0:
j = left
while j > 0:
lowbit1 = ord(num2[j - 1]) - 48
lowbit2 = lowbit1 + shift
if lowbit2 > 9:
lowbit2 = lowbit2 - 10
shift = 1
else:
shift = 0
newstr = str(lowbit2) + newstr
j = j - 1
if shift == 1:
newstr = str(shift) + newstr
return newstr
class Solution:
    def addStrings(self, num1: str, num2: str) -> str:
        l1 = len(num1)
        l2 = len(num2)
        if l1 > l2 :
            num1, num2 = num2, num1
            l1, l2 = l2 , l1
        shift = 0
        newstr = ''
        i = l1
        while i > 0:
            lowbit1 = ord(num1[l1 - (l1 - i + 1)]) - 48
            lowbit2 = ord(num2[l2 - (l1 - i + 1)]) - 48
            lowbit3 = lowbit1 + lowbit2 + shift
            if lowbit3 > 9:
                lowbit3 = lowbit3 - 10
                shift = 1
            else:
                shift = 0
            newstr = str(lowbit3) + newstr
            i = i - 1
        left = l2 - l1
        if left > 0:
            j = left
            while j > 0:
                lowbit1 = ord(num2[j - 1]) - 48
                lowbit2 = lowbit1 + shift
                if lowbit2 > 9:
                    lowbit2 = lowbit2 - 10
                    shift = 1
                else:
                    shift = 0
                newstr =  str(lowbit2) + newstr
                j = j - 1
        if shift == 1:
            newstr = str(shift) + newstr
        return newstr

这真是太糟糕了…… 做完题目我发现,一年前我就做过这个题目,那时候是用 go 语言写的:

import "strconv"
func addStrings(num1 string, num2 string) string {
i := len(num1) - 1
j := len(num2) - 1
result := ""
for i >= 0 && j >= 0 {
sum := int(num1[i]) - 48 + int(num2[j]) - 48 + c
if sum < 10 {
result = strconv.Itoa(sum) + result
} else {
result = strconv.Itoa(sum % 10) + result
for i >= 0 {
sum := int(num1[i]) - 48 + c
if sum < 10 {
result = strconv.Itoa(sum) + result
} else {
result = strconv.Itoa(sum % 10) + result
for j >= 0 {
sum := int(num2[j]) - 48 + c
if sum < 10 {
result = strconv.Itoa(sum) + result
} else {
result = strconv.Itoa(sum % 10) + result
if c > 0 {
result = "1" + result
return result
import "strconv"

func addStrings(num1 string, num2 string) string {
    i := len(num1) - 1
    j := len(num2) - 1
    c := 0
    result := ""
    for i >= 0 && j >= 0 {
        sum := int(num1[i]) - 48 + int(num2[j]) - 48 + c
        if sum < 10 {
            c = 0
            result = strconv.Itoa(sum) + result
        } else {
            c = 1
            result = strconv.Itoa(sum % 10) + result
        }
        i --
        j --
    }
    
    for i >= 0 {
        sum := int(num1[i]) - 48 + c
        if sum < 10 {
            c = 0
            result = strconv.Itoa(sum) + result
        } else {
            c = 1
            result = strconv.Itoa(sum % 10) + result
        }
        i --
    }
    
    for j >= 0 {
        sum := int(num2[j]) - 48 + c
        if sum < 10 {
            c = 0
            result = strconv.Itoa(sum) + result
        } else {
            c = 1
            result = strconv.Itoa(sum % 10) + result
        }
        j --
    }
    
    if c > 0 {
        result = "1" + result
    }
    
    return result
}

也够糟糕的,但是我看了一下,同时用了两个指针做第一遍遍历,那就比这次还好一点,可见,编码能力是退化了的。

这道题目虽然直觉上非常简单,无非就是笔算加法的代码表达,如果真的这么去写,恐怕就会写成我那个样子。还是需要有一点数据结构和算法的知识。

这道题里,数据结构是字符串,字符串的本质是一个字符的数组。所以,这道题的数据结构就是数组。然后我们计算加法,是从个位加到十位百位的,对应字符串数组,是从数组的最后一个元素,向数组的第一个元素遍历,是一个逆序遍历数组的问题,只不过要同时遍历两个,还要处理边界条件。

不管用什么语言,这里还有一个字符串 string,字符 char,整数 int 三者之间的转换方法之类的问题。属于一门语言的基础知识,所以这道题要顺顺利利快速做出来,其实要求是很高的。没点积累搞不定,忘记了基础也搞不定。

然后我又写了一遍:

class Solution:
def addStrings(self, num1: str, num2: str) -> str:
i = len(num1) - 1
j = len(num2) - 1
result = ""
carry = 0
while i >= 0 and j >= 0:
add = int(num1[i]) + int(num2[j]) + carry
carry = add // 10
add = add % 10
result = str(add) + result
i = i - 1
j = j - 1
if i >= 0:
while i >= 0:
add = int(num1[i]) + carry
carry = add // 10
add = add % 10
result = str(add) + result
i = i - 1
if j >= 0:
while j >= 0:
add = int(num2[j]) + carry
carry = add // 10
add = add % 10
result = str(add) + result
j = j - 1
if carry > 0:
result = "1" + result
return result
class Solution:
    def addStrings(self, num1: str, num2: str) -> str:
        i = len(num1) - 1
        j = len(num2) - 1
        result = ""
        carry = 0
        while i >= 0 and j >= 0:
            add = int(num1[i]) + int(num2[j]) + carry
            carry = add // 10
            add = add % 10
            result = str(add) + result
            i = i - 1
            j = j - 1
        if i >= 0:
            while i >= 0:
                add = int(num1[i]) + carry
                carry = add // 10
                add = add % 10
                result = str(add) + result
                i = i - 1
        if j >= 0:
            while j >= 0:
                add = int(num2[j]) + carry
                carry = add // 10
                add = add % 10
                result = str(add) + result
                j = j - 1
        if carry > 0:
            result = "1" + result
        return result

差不多这水平吧,比第一段好多了,这里首先选用了整数除法作为计算进位的方法,而不是像第一段里写的那样,用大于 10 做判断,用了取模来取单个数位求和的个位,这是一个数学小知识,而不是用减 10,我第一段不是故意写那么烂的,是真的就那么烂忘记这些事情了。用整除法和取模,计算的效率未必高,因为是除法和移位之类的,比起用 if 哪个效率高不好说,但是一个很大的好处是,写法非常统一。然后,我又写错了 N 次的边界,终于对了。

这时候,就发现,代码重复得很厉害。然后我把重复得代码合并了一下:

class Solution:
def addStrings(self, num1: str, num2: str) -> str:
i = len(num1) - 1
j = len(num2) - 1
result = ""
carry = 0
add = 0
while i >= 0 or j >= 0:
if i >= 0:
add = add + int(num1[i])
i = i - 1
if j >= 0:
add = add + int(num2[j])
j = j - 1
add = add + carry
carry = add // 10
add = add % 10
result = str(add) + result
add = 0
if carry > 0:
result = "1" + result
return result
class Solution:
    def addStrings(self, num1: str, num2: str) -> str:
        i = len(num1) - 1
        j = len(num2) - 1
        result = ""
        carry = 0
        add = 0
        while i >= 0 or j >= 0:
            if i >= 0:
                add = add + int(num1[i])
                i = i - 1
            if j >= 0:
                add = add + int(num2[j])
                j = j - 1
            add = add + carry
            carry = add // 10
            add = add % 10
            result = str(add) + result
            add = 0

        if carry > 0:
            result = "1" + result
        return result

变成了这样,就短了很多了,算法得效率没变。只是写起来简洁多了。然后仔细观察一下,发现,add 变量和 carry 变量是可以合并的,去掉一个:

class Solution:
def addStrings(self, num1: str, num2: str) -> str:
i = len(num1) - 1
j = len(num2) - 1
result = ""
carry = 0
while i >= 0 or j >= 0:
if i >= 0:
carry += int(num1[i])
i = i - 1
if j >= 0:
carry += int(num2[j])
j = j - 1
result = str(carry % 10) + result
carry //= 10
if carry > 0:
result = "1" + result
return result
class Solution:
    def addStrings(self, num1: str, num2: str) -> str:
        i = len(num1) - 1
        j = len(num2) - 1
        result = ""
        carry = 0
        while i >= 0 or j >= 0:
            if i >= 0:
                carry += int(num1[i])
                i = i - 1
            if j >= 0:
                carry +=  int(num2[j])
                j = j - 1
            result = str(carry % 10) + result
            carry //= 10
        if carry > 0:
            result = "1" + result
        return result

到这里,应该已经是我的极限了,当然,可以利用一些语法糖,写得再短一点,我觉得那就纯粹是炫技了,说明作者对语法糖的熟练,也是好的,不过从代码运行角度看,没什么区别,从可读性上看,哪个好还不一定。

这个题目里复习了哪些知识呢?

  1. Python 字符串、字符转换成整数 int() 函数,转换成字符串 str() 函数;
  2. Python 整除的写法是 // 如果用一个 / 回得到 浮点数;
  3. 两个线性数据结构的同时遍历,可以用两个指针(下标)一起遍历;

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK