leetcode2. 两数相加

leetcode2.题目描述
  给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。

  如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。

  您可以假设除了数字 0 之外,这两个数都不会以 0 开头。

  今天leetcode上的每次一题其实是归并有序链表,但是比较简单,而且我上次已经把归并有序链表说的比较详细了,可移步
leetcode23. 合并K个排序链表
  为了表示我心里为学习的热爱,就又做了这题。
解题思路:
  链表和二叉树的题呢做多了会发现很多用递归很容易解决,刚开始我也很难理解递归,咋想到的呢,写多了也发现,递归真的是最容易理解的写法了,反而不用递归写起来更困难,像二叉树的遍历,递归三行,不递归十几行,逻辑还复杂,所以说递归是一种偷懒的写法也不为过了。那先简单聊聊我对递归的拙见吧。

  我相信大多数人刚学递归,都想着把递归程序的每一步做了什么,给他分析得明明白白,模拟计算机在脑子或者在草稿纸上给他推一遍,这往往能把人绕晕,让人望而却步。我觉得这是学递归的一个误区,越是想知道递归里面干了啥,和纠结一些细节,或者某个变量,越是不能理解程序到底干了什么,往往学递归,需要在大局观上看问题。写了个伪代码框架,仅供观赏,反正我就是经常这么来的。

def func(data):
	#递归终止条件
	if data满足一定条件:
		return 
	子问题答案 = func(子数据)
	最终答案 = 结合当前答案和子问题答案
	return 最终答案

  举个例子,来个简单的,主要是说明下如何用这个套路:如何对一个数组的所有求和吧,假如数组是[1,3,5,6,9],当前答案就是1,子问题就是对[3,5,6,9]求和[3,5,6,9],再往后你就不用考虑了。所以我们要考虑的问题就是,add([1,3,5,6,9])怎么分解为1和子问题add([3,5,6,9]),很显然用1+add([3,5,7,9]),这个题比较简单;另外还有一个问题就是,终止条件是什么,这里的终止条件就是当数组长度为1,无法使用递归函数了,直接返回自己的就可以。所以有如下程序:

def _add(nums):
	if len(nums)==1:
		return nums[0]
	sub_answer = _add(nums[1:])
	final_answer = nums[0]+sub_answer
	return 	final_answer 

  主要看思想哈,这个问题用循环或者内置函数更简单,可是当遇到复杂的问题,递归可就简单多了。
  回归到今天的问题吧‘’两数相加,先假设一个简单的情况吧,就是来那个链表的长度相同,现在的问题是如何相加两个链表l1和l2,假设去掉两个链表的两个头结点,剩下节点的相加,是不是一个子问题,要做的事情和add(l1,l2)是一样的。所以递归的框架是不是就出来了。

  这需要每次把前面每层的进位情况,加入到下次的子问题中,初始的进位肯定为0;另外最终需要返回头结点,可以把l2加入到l1中来,最后返回l1,然后每次子问题返回的也是子问题的头结点,所以你需要把l1头结点指向子问题得到的头结点,这就是这个问题中融合当前答案和子问题答案的方式,每个问题不一样,像上面的相加,就是通过相加来结合的。基本思路就出来了。

  但是我上面只是说两个链表长度相同的情况,两个一块结束;如果不一块结束,我们还需要对另外一个链表接着递归呀,这里递归逻辑和两个链表的差不多,只不过是另外一个链表变成0了,所以前面需要添加一些判断条件,判断哪个链表结束了,还是一起结束的。

看代码吧

class Solution:
    def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
        def _addTwo(l1, l2, res):
        	#l1结束,l2没有结束的递归逻辑
            if not l1 and l2:
                if res==0 :
                    return l2
                else:
                    l2.val += res
                if l2.val>=10:
                    l2.val-=10
                    res = 1
                else:
                    res = 0
                head = _addTwo(None, l2.next,res)
                l2.next = head
                return l2
			#l2结束,l1没有结束的递归逻辑
            if not l2 and l1:
                if res==0 :
                    return l1
                else:
                    l1.val += res
                if l1.val>=10:
                    l1.val-=10
                    res = 1
                else:
                    res = 0
                head = _addTwo(l1.next, None, res)
                l1.next = head
                return l1
			#l2和l1都结束,算是终止条件了,这里需要判断是否有进位,如果有,我们需要返回一个val为1,next为None的节点
            if (not l1) and (not l2):
                if res==1:
                    return ListNode(1)
                else:
                    return None

			#l1和l2都没有结束的递归逻辑
            l1.val += l2.val+res
            if l1.val>=10:
                l1.val-=10
                res = 1
            else:
                res = 0
            #子问题的解
            head = _addTwo(l1.next, l2.next,res)
            #把当前头结点指向子问题的头结点
            l1.next = head
            return l1
        #调用递归函数,并传入初始进位res为0
        return _addTwo(l1, l2,0)

  个人经验就是这种题真的是多写写就会了呀,基本不用看答案了。问问大佬们,为啥感觉每次空间复杂度都挺高的,我这题也没用额外的数据类型呀,全部都是原地操作。

在这里插入图片描述

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值