(Python3)数据结构——09.单链表之计算两数之和

前言

  • 有Python基础
  • 有数据结构单链表基础,不清楚的参考前面的博客
  • 链接: https://blog.csdn.net/sf9898/article/details/104946291

问题描述

  • 这是某大厂的面试题,现场写代码的那种。
给出两个非空的链表,用来表示两个非负的整数,他们的位数是按逆序存储的,
并且每一个节点都只能存储一个数字。如果将这两个数相加,则会返回一个新的链表
来表示他们的和。可以假设除了数字0之外,这两个数都不会以0开头。
  • 输入输出这一块划重点,这里的输入输出是比较抽象的,大致意思对了就行,不是牛客网那种格式化输入。
输入(1 -> 6 -> 4)+ (3 -> 1 -> 2)
输出 (4 -> 7 -> 6)
原因是  461+213 = 674

分析及实现

part1.分析与定义

  • 题目要求是用的链表,那么首先先定义链表类。
  • 关于数据的合法性:一般不会为难人,给的都会是合法数据,不用判断负数。也不用纠结要是节点存不是一个数字的情况。输入的都只能是合法的字符。
  • 关于输入,怎么输入就怎么存,也就是说输入也是逆序的。
class Node(object):
    def __init__(self, item):
        self.item = item
        self.next = None


class Link(object):
    def __init__(self):
        self.__head = None
  • 接下来再写几个可能会用到的函数。比如插入和删除等。题目中说位数是按照逆序存储的,那么假设从尾部插入,第一个数进来1,第二次进来6,第三次进来4,那么根据这个想法,得到的链表从头到尾是不是164,是不是符合题意?实际上表示的数是461,实现了逆序存储,因此在插入这一块应该写尾部插入。
    def addBack(self, item):
        node = Node(item)
        if self.__head is None:
            self.__head = node
            return
        cur = self.__head
        pre = None
        while cur:
            pre = cur
            cur = cur.next
        pre.next = node
  • 常见的判空和遍历应该也写上去有助于检查验证。
   def isEmpty(self):
        return self.__head is None
 # 定义一个遍历函数用来验证
    def travel(self):
        cur = self.__head
        while cur:
            print(cur.item, end=' ')
            cur = cur.next
        print('')
  • ok ,再考虑下删除和相加的事。如果直接提取两个数相加可以吗?小数字貌似可以,但是大的数字呢?看看自己的计算机是几位的。一万个、一千个1也是合法数据,照样得算,照样得有结果。因此不能采用提取各个节点的item然后拼起来形成一个数相加的方法。应该回归到小学数学,回到最初的起点。第一个节点是个位,后面的是十位百位…那么一个一个删除提取出来做加法,有进位的话要注意。因此在Link类里面应该有删除的方法,而且这个方法还要有返回值(不然怎么算)。
    def removeFront(self):
        # 返回值是一个节点的item,空节点的话返回0,这个函数要做到删除,并且有返回值
        cur = self.__head
        if cur:
            self.__head = self.__head.next
            return cur.item
        else:
            return 0
  • 上面代码的写法是为了防止三位数加两位数的这种类似的情况。
  • “如果将这两个数相加,则会返回一个新的链表来表示他们的和。”从题目的这点要求可以看出,我们需要设置一个函数,接收的参数是两个链表的对象(当然,写别的好像也行,只要能实现)。返回值是一个新的链表,当然,这个链表依旧是逆序存储。
def addTwoNum(l1, l2):
    # 参数是两个链表的对象,只要两个链表都不是空那就可以算,3位加2位的话,第二个数的百位就当做0
    ll = Link()
    addNum = 0  # 进位
    while l1.isEmpty() == False or l2.isEmpty() == False:
        temp = l1.removeFront() + l2.removeFront() + addNum
        addNum = 0
        if temp >= 10:
            # 大于等于10要进位哦
            addNum = 1
            temp = temp - 10
        ll.addBack(temp)  # 存进去
    return ll

part2.完整代码测试一下

# 给出两个非空的链表,用来表示两个非负的整数,他们的位数是按逆序存储的,   --一般给的都会是合法数据,不会为难人
# --这一点说明从头到尾应该是164(实际上这个链表表示的数是461)  312(213)  476(674)
# 第一次输入的数是1,之后6,然后4,因此是尾部插入
# 并且每一个节点都只能存储一个数字。如果将这两个数相加,则会返回一个新的链表  --item只能存一个数
# 来表示他们的和。可以假设除了数字0之外,这两个数都不会以0开头。
# 相加怎么进行?直接取出来加??这样是不行的,因为PC的计算范围是有限的,想想自己的电脑是几位的机子。
# 一千个1加上一千个1理应是一千个2,这样的数是合法的。因此应当回归到最初的起点,用最原始的小学的数学计算去做。
# 先取出个位数,即开头,然后十位百位...但是这里又要注意,万一两个数的位数是不一样的呢?
# 从取数字的角度来讲,应该是要取开头

# 输入(1 -> 6 -> 4)+ (3 -> 1 -> 2)
# 输出 (4 -> 7 -> 6)
# 原因是  461+213 = 674


class Node(object):
    def __init__(self, item):
        self.item = item
        self.next = None


class Link(object):
    def __init__(self):
        self.__head = None

    def isEmpty(self):
        return self.__head is None

    def addBack(self, item):
        node = Node(item)
        if self.__head is None:
            self.__head = node
            return
        cur = self.__head
        pre = None
        while cur:
            pre = cur
            cur = cur.next
        pre.next = node

    def removeFront(self):
        # 返回值是一个节点的item,空节点的话返回0,这个函数要做到删除,并且有返回值
        cur = self.__head
        if cur:
            self.__head = self.__head.next
            return cur.item
        else:
            return 0

    # 定义一个遍历函数用来验证
    def travel(self):
        cur = self.__head
        while cur:
            print(cur.item, end=' ')
            cur = cur.next
        print('')


def addTwoNum(l1, l2):
    # 参数是两个链表的对象,只要两个链表都不是空那就可以算,3位加2位的话,第二个数的百位就当做0
    ll = Link()
    addNum = 0  # 进位
    while l1.isEmpty() == False or l2.isEmpty() == False:
        temp = l1.removeFront() + l2.removeFront() + addNum
        addNum = 0
        if temp >= 10:
            # 大于等于10要进位哦
            addNum = 1
            temp = temp - 10
        ll.addBack(temp)  # 存进去
    return ll


def main():
    l1 = Link()
    l2 = Link()
    N1 = int(input())  # 这个用来接收用户的输入,代表第一个数的位数(第一个链表的节点数)
    # 接下来输入1 6 4(类似这样子),假设是这样,那么N1 = 3,下面循环三次,接收三个输入,尾插三次
    for i in range(N1):
        l1.addBack(int(input()))
    N2 = int(input())  # 第二个数的位数
    for i in range(N2):
        l2.addBack(int(input()))
    # 写到这里可以利用遍历函数初步验证添加是否成功、是否逆序存储
    l1.travel()  # 1 6 4
    l2.travel()  # 3 1 2
    # 希望调用一个函数直接计算出结果并返回一串链表
    ll = addTwoNum(l1, l2)
    ll.travel()


if __name__ == '__main__':
    main()

part3.根据代码中的设置,输入数据验证

test1

输入:
3
1
2
3
2
4
5
输出:
1 2 3
4 5
5 7 3
  • 输入的第一个数是N1,是第一个数的位数。接下来三个数是123(表示的是321,因为链表里面逆序存储)。之后是2,是N2,是第二个数的位数。接下来两个数是45,表示54。因此在进行加法之后,321+54 = 375,代码中ll.travel()的结果应是5 7 3。结果如下。

在这里插入图片描述

test2

  • 再验证下题目的数据
输入:
3
1
6
4
3
3
1
2
输出:
1 6 4 
3 1 2 
4 7 6 

在这里插入图片描述

test3

  • 为了验证进位的情况,再设置了一组测试数据,7 3 1(实际表示137),4 6(实际是64),137+64=201,因此结果输出应该是1 0 2
输入:
3
7
3
1
2
4
6
输出:
7 3 1 
4 6 
1 0 2 

在这里插入图片描述

  • 如有问题,还望批评指出
发布了28 篇原创文章 · 获赞 12 · 访问量 4122

猜你喜欢

转载自blog.csdn.net/sf9898/article/details/104998367