上次面试之后,因为种种原因,没来得及把最后一道题系统地给大家讲解一下,今天有空,就赶快写博给大家分享,谢谢大家的支持!(完整代码在文章最后。)
上次面试的链接:
2020年毕业生腾讯校园招聘提前批——后台开发——面试经验——分享给大家交流经验。
废话不多说,下面进入正题,题目是:使用python3+链表,做数学的相加(大数相加)。
这个地方请不要和我抬杠,当然,Python可以计算很大的数据,这个谁都知道,现在是来讲解这个算法,不是说Python无法计算那么大的数字,今天的重点是算法,算法,算法!!!
链表之前我也写了个博客,链接在这里:
Pyhon3的链表的实现(包含遍历,读取,删除,头插,尾插)
基于上面的链表的实现,我们可以去直接使用上次的链表的代码,来完成我们的大数相加。
基本思路:
首先我们要了解并且明白一件事情:我们进行加减乘除是先加个位,后加十位,然后百位等等。永远是最低位先相加。而链表中,根据单向链表的特性,永远是距离头节点最近的那一个逻辑下标为0的先读取到,所以在存储数据时,我们应当把个位放在下标为0的位置,十位在下标1的位置,百位下标2,以此类推。
上面的明白以后,我们就可以讨论怎么存储了,在C语言中,一个数据存储大小有限,所以应该读取一个数字的字符串如字符串6874156987516435类似的,然后有两种方案:
第一种:循环读取直到 '\0' 这个字符,即尾部的结束符,然后每读取一个使用头插法,就能保证个位距离头部最近。
第二种:计算字符串长度,从尾部读取,然后使用尾插法。
显而易见,对于C语言来讲,明显是第一种较好,省去了很多不必要的计算。
但是,我们现在使用的是Python,我们可以读取一个非常大的数字,虽然Python可以直接存储很大的数据,但是Python的输入默认是字符串,如果全部直接int()转成整形,在循环取整取余,明显有很大的麻烦,所以我们使用C语言的第一种方式,但是Python有遍历,不需要判断最后一个字符是否是 '\0' ,直接遍历读取:
MyList1 = SingleLinkList()
num = input("输入数据:")
for i in num:
MyList1.add(int(i))
MyList1.travel()
上图就是那串代码的运行结果(不要在意那个命令行的名字,我在笔试的代码的文件夹里写的这个。)
注意:我们使用了上边引用的另外两篇博客其中之一的链表的代码!!!!!!
现在我们有了这样的一个链表,并且个位在第一位了。
正题开始:
那么重点就是,怎么相加,有这么几种情况:
第一:第一个数据比第二个数据长度短很多,比如(2131, 244132415)两个数。
第二:和第一种情况相反。
第三:有进位或连续进位,比如:9999+9999999这种情况。
先把两个数据输入再说,复制上面的代码两份:
MyList1 = SingleLinkList()
num = input("输入数据1:")
for i in num:
MyList1.add(int(i))
MyList1.travel()
MyList2 = SingleLinkList()
num = input("输入数据2:")
for i in num:
MyList2.add(int(i))
MyList2.travel()
现在我们完成了数据的输入,我们知道:计算肯定是一个一个的读取的,并不是所有全部读取,这时候就用到了我们的读取的方法:
# 上面的省略
def read_next(self):
"""一次读一个"""
cur = self.__head # 用来记录当前的节点
while cur != None:
# print(cur.elem, end=' ')
yield cur.elem
cur = cur.next
yield None
# 下面的省略
调用这个,我们能拿到一个生成器,每次读取一个,使用Python的内建方法 next() 来完成一个数据的读取。
MyList1_num = MyList1.read_next()
MyList2_num = MyList2.read_next()
num1 = next(MyList1_num)
num2 = next(MyList2_num)
我们还需要两个数,一个存放进位,另一个存放结果:
C1 = 0 # 代表进位
sum = 0 # 代表两个数字的和
再下一个,需要一个存放结果的链表,需要特别注意的是:存放结果的链表,要把个位放在最后。而我们计算时是先计算的个位,后计算的低位,所以我们还是需要使用头插法。
ResList = SingleLinkList()
接下来我们就可以计算了,由于是所有都要计算,所以必须使用到循环:
while num1 and num2:
sum = num1 + num2 + C1 # 求和
C1 = sum // 10
sum = sum % 10
ResList.add(sum) # 存放结果的链表
num1 = next(MyList1_num)
num2 = next(MyList2_num)
每次相加,我们都需要把进位加上,然后进位拿到非个位部分,这时候保证我们每次插入的是一个一位的数据,而不是直接插入一个超过一位的。
上面的还不够,还记得我们刚刚列举的三种情况的前两个:即两个数的长短不一样。
所以我们还需要判空:
if num1:
while num1:
C1 = (num1+C1) // 10
num1 = (num1+C1)%10
ResList.add(num1)
num1 = next(MyList1_num)
if num2:
while num2:
C1 = (num2+C1) // 10
num2 = (num2+C1)%10
ResList.add(num2)
num2 = next(MyList2_num)
因为当数据没有后,生成器会返回空,所以直接判空后,让剩余部分对进位相加,继续取整取余完成剩下的运算。
这样够了吗?上面列举的三种情况的最后一个:9999+999999时,经过上面的过程后,进位上还有一个数据,我们还需要把进位也插入结果链表中,最后读取结果链表,就完成了大数相加:
if C1:
ResList.add(C1)
ResList.travel() # 遍历结果链表
完整代码如下(其中用得到的用不到的我都已经标注明白了,除去用不到的代码总共不到70行。):
# -*- coding:utf-8 -*-
class SingleNode(object):
"""单链表的节点"""
def __init__(self, elem):
self.elem = elem # 存放数据元素
self.next = None # 指向下一个数据元素
class SingleLinkList(object):
"""单链表的类"""
def __init__(self, node=None):
self.__head = node
self.__length = 0 # 存放长度,因为这个程序里用不到,所以不需要也可
def is_empty(self): # 可以删除,这个程序用不到
"""链表是否为空"""
return self.__head == None
def length(self): # 可以删除,这个程序用不到
"""链表长度"""
return self.__length
def travel(self): # 这个程序用到了这个
"""遍历整个链表"""
cur = self.__head # 用来记录当前的节点
while cur != None:
print(cur.elem, end='')
cur = cur.next
print()
def read_next(self): # 这个程序用到了这个
"""一次读一个"""
cur = self.__head # 用来记录当前的节点
while cur != None:
# print(cur.elem, end=' ')
yield cur.elem
cur = cur.next
yield None
def add(self, item): # 这个程序用到了这个
"""在头部添加元素, 头插法"""
node = SingleNode(item)
node.next = self.__head
self.__head = node
self.__length += 1
def append(self, item): # 可以删除,这个程序用不到
"""在尾部添加元素,尾插法"""
node = SingleNode(item)
if self.is_empty():
self.__head = node
else:
cur = self.__head
while cur.next != None:
cur = cur.next
cur.next = node
self.__length += 1
MyList1 = SingleLinkList()
num = input("输入数据1:")
for i in num:
MyList1.add(int(i))
MyList1.travel()
MyList2 = SingleLinkList()
num = input("输入数据2:")
for i in num:
MyList2.add(int(i))
MyList2.travel()
MyList1_num = MyList1.read_next()
MyList2_num = MyList2.read_next()
num1 = next(MyList1_num)
num2 = next(MyList2_num)
ResList = SingleLinkList()
C1 = 0 # 代表进位
sum = 0 # 代表两个数字的和
while num1 and num2:
sum = num1 + num2 + C1 # 求和
C1 = sum // 10
sum = sum % 10
ResList.add(sum)
num1 = next(MyList1_num)
num2 = next(MyList2_num)
if num1:
while num1:
C1 = (num1+C1) // 10
num1 = (num1+C1)%10
ResList.add(num1)
num1 = next(MyList1_num)
if num2:
while num2:
C1 = (num2+C1) // 10
num2 = (num2+C1)%10
ResList.add(num2)
num2 = next(MyList2_num)
if C1:
ResList.add(C1)
ResList.travel() # 遍历结果链表
写在最后:
大数计算不难,链表也不难,相信自己,世上无难事,只怕有心人!!!