2024.8.22 Python,链表两数之和,链表快速反转,二叉树的深度,二叉树前中后序遍历,N叉树递归遍历,翻转二叉树

1.链表两数之和

输入:l1 = [2,4,3], l2 = [5,6,4]
输出:[7,0,8]
解释:342 + 465 = 807.
示例 2:
输入:l1 = [0], l2 = [0]
输出:[0]
示例 3:
输入:l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9]
输出:[8,9,9,9,0,0,0,1]
昨天的这个题,用自己的办法写的麻烦的要死,然后刚才一看chat归类的办法,感觉自己像个智障。

class ListNode:
	def __init__(self,val=0,next=None):
		self.val=val
		self.next=next

class Solution1:
	def addTwoNumbers(self,l1:Optional[ListNode],l2:Optional[ListNode])->Optional[ListNode]:
		ans=ListNode(-1)
		head=ans
		carry=0

		while l1 or l2 or carry:
			sum_val=carry
			if l1:
				sum_val+=l1.val
				l1=l1.next
			if l2:
				sum_val+=l2.val
				l2=l2.next
			
			carry=sum_val//10
			ans.next=ListNoed(sum_val%10)
			ans=ans.next

		return head.next

代码逻辑:我的代码中的各种冗余都删掉了,就是说,公共的操作是可以提出来的,在我的代码中,我可以把ans.next的计算放到后面,然后设定sum_val,有谁就加谁,然后最后处理carry,还有sum_val这样的处理。也就是while l1 and l2:还有while l1,while l2.这样的三个情况直接分开。

2.链表翻转:

class ListNode:
	def __init__(self,val=0,next=None):
		self.val=val
		self.next=next
class Solution2:
	def reverseList(self,head:Optional[ListNode])->Optional[ListNode]:
		prev=None
		current=head

		while current:
			next_node= current.next  #1.以current.next.val为头的全部链表存入next_node
			current.next=prev		 #2.也就是说此时的current只剩下了头,next都变成了空
			prev=current			 #3.prev成了current的第一个指针
			current=next_node 		 #4.current现在没了第一个值,只剩current.next以后得部分了
		return prev

假设链表为1->2->3那么,四步操作,next_node就等于2->3,current.next=None, prev=1, current=2->3
第二次,next_node=3,current.next=1, prev2->1,current=3,以此类推变成3->2->1
代码逻辑如下:
1.存后:先存之后的值,目的是将头和next分离
2.剪接:next重新定义为之前处理过的数据,剪接操作,让current成为新的链表。
3.存新:这时候current已经是新的链表了,那么把新的链表存进prev中,current就可以被释放了
4.读复:用current去覆盖最开始没有头的链表,进入下一个循环

3.链表两数相加 II

输入:l1 = [7,2,4,3], l2 = [5,6,4]
输出:[7,8,0,7]
示例2:
输入:l1 = [2,4,3], l2 = [5,6,4]
输出:[8,0,7]
示例3:
输入:l1 = [0], l2 = [0]
输出:[0]
这个题就是1的翻转,那么完全可以用2加1的办法进行计算我这里直接调用:

class Solution:
    def addTwoNumbers(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]:
        sol1 = Solution1()
        sol2 = Solution2()
        l1 = sol2.reverseList(l1)
        l2 = sol2.reverseList(l2)
        ans = sol1.addTwoNumbers(l1, l2)
        ans = sol2.reverseList(ans)
        return ans

这是这个题的调用选项,没什么好说的,就是把第一题和第二题合起来用了,那么接下来我将给出一个专属于这个题的解法,这个题中需要学会给头部创建新的结点这个用法,可以直接用于链表的创建。以及divmod这个函数的使用

class Solution:
	def addTwoNumbers(self,l1:Optional[ListNode],l2:Optional[ListNode])->Optional[ListNode]:
		s1,s2=[],[]
		while l1:
			s1=s1.append(l1.val)
			l1=l1.next
		while l2:
			s2=l2.append(l2.val)
			l2.l2.next
		carry=0
		dummy=ListNode(0)
		while s1 or s2 or carry:
			sum=(s1.pop() if s1 else 0) + (s2.pop() if s2 else 0) +carry
			val,carry=divmod(sum,10)
			dummy.next=ListNode(val,dummy.next)
		return dummy.next			 

代码逻辑如下:
1.首先是用两个数组去存,用append动态存,存进去以后就没l1什么事了
2.定义好新的链表,然后当数组还有carry任意一个不为空的时候就进入循环,然后算sum,算商算余数,其中divmod函数是被除数和除数等于商和余数,非常的符合直觉非常的好用
3.其中dummy.next这个代码非常的好,他是这样的理解的相当于node=ListNode (val, dummy.next)然后再是dummy.next=node,其中的逻辑是,用ListNode这样写,就可以在dummy.next的前面建立一个新头,然后把dummy.next的指针移动到现在头的位置,为下一次循环做基础,也就是说,通过这个代码,我们可以继续改写第二个题。

4.翻转链表:

class Solution:
	def reverseList(self,head:Optional[ListNode])->Optional[ListNode]:
		dummy=ListNode()
		while head:
			dummy.next=ListNode(head.val,dummy.next)
			head=head.next

非常的完美,非常非常的完美,就是这样

5.二叉树,在8.19的时候第一次提出

class TreeNode:
	def __init__(self,val=0,left=None,right=None):
		self.val=val
		self.left=left
		self.right=right

class Solution:
	def maxDepth(self,root:Optional[TreeNode])->int:
		if not root:
			return 0
		else:
			left_height=self.Depth(root.left)
			left_height=self.Depth(root.right)
			return max(left_height,right_height)+1

讲道理,再看二叉树确实有种秒杀了的感觉,如果是链表的话,我们可能会考虑用while去数数,但是你现在要考虑一个事情,在链表里,你永远只有一种可能,所以你可以使用while来进行搜索,但是现在你变成二叉树了以后,不是变成了两种可能性,而是直接变成了2的次方的可能性,所以就必须要使用递归来进行可能性的所有遍历,而这就是回溯算法的优势所在,也就是说你不知道要做多少次while的嵌套的时候,那就使用回溯算法来嵌套函数
if not root这个还是很重要,回溯算法的尽头,根节点的时候,下一个已经是none了,那么就不用再加了。
最小深度的代码如下:

class Solution:
    def minDepth(self, root: TreeNode) -> int:
        if not root:
            return 0
        
        # 计算左子树和右子树的最小深度
        left_depth = self.minDepth(root.left)
        right_depth = self.minDepth(root.right)
        
        # 如果其中一个子树为空,则返回不为空的子树的深度+1
        if not root.left or not root.right:
            return left_depth + right_depth + 1
        
        # 否则,返回左右子树中较小的深度,并加一
        return min(left_depth, right_depth) + 1

8.19的文章并没能说清楚这个代码的逻辑,我将在今天的文章中详细解释这个代码逻辑
1.基础的逻辑和最大深度类似,不过是找最小深度的叶子结点,前面用的是max,现在就要用min,但是相比于max的话,min的限制就会稍微多一些,max里我根本不需要考虑一个节点是单个子节点的情况,因为max的过程中会自动跳过那个没有子节点的一侧,但是在最小深度中,如果你不判断,那么他就会在min的时候把0选出来,所以加if not root.left or not root.right的目的就是为了避免出现0
2. if not root就是,上一个节点直接就是子节点了,也就是说上一个节点没有任何一个子节点
3. left_depth函数和right_depth函数和上一个办法类似,没什么好说的
4. if not这里就是我刚说的,如果一个节点有null也有子节点,那么他就得继续下去,所以他的处理就是return left_depth+right_depth+1,因为他并不知道是谁有谁没有,索性直接统一算得了。
5. return min+1这步没什么好说的。

6.二叉树的前序中序后序遍历

这个题中我将详细的介绍每一种遍历的代码,其中每一种遍历的顺序请参考哔哩哔哩的视频,视频地址如下:

https://www.bilibili.com/video/BV1Ub4y147Zv/?spm_id_from=333.337.search-card.all.click

关于代码,我是学习的LeetCode的【Python3】二叉树所有遍历模板及知识点总结(参考大佬们)链接如下:

https://leetcode.cn/problems/binary-tree-inorder-traversal/solutions/324347/python3-er-cha-shu-suo-you-bian-li-mo-ban-ji-zhi-s

**方法一:**递归,控制顺序从而实现遍历

class Solution:
    def preorderTraversal(self, root: TreeNode) -> List[int]:
        if not root:
            return []
        # 前序递归
        return [root.val] + self.preorderTraversal(root.left) + self.preorderTraversal(root.right)
        # # 中序递归 
        return self.inorderTraversal(root.left) + [root.val] + self.inorderTraversal(root.right)
        # # 后序递归
        return self.postorderTraversal(root.left) + self.postorderTraversal(root.right) + [root.val]

代码逻辑:
1.前序遍历是根,左,右,中序遍历是左,根,右,后序遍历是左,右,根。根据哔哩哔哩那个视频进行记忆,就非常的快。
2.not root还是用来停止最后代码的,然后最后的叶子结点,因为带入root.left和root.right的时候,就会return []所以根本不用管他。最后就只输出val
**方法二:**递归,深度优先搜索

class Solution:
    def preorderTraversal(self, root: TreeNode) -> List[int]:
        def dfs(cur):
            if not cur:
                return      
            # 前序递归
            res.append(cur.val)
            dfs(cur.left)
            dfs(cur.right) 
            # 中序递归
            dfs(cur.left)
            res.append(cur.val)
            dfs(cur.right)
            # 后序递归
            dfs(cur.left)
            dfs(cur.right)
            res.append(cur.val)      
        res = []
        dfs(root)
        return res

**方法三:**迭代,使用栈来模拟递归的过程,该方法仅适用于前后序遍历,是前后序的最简单的方法

class Solution:
	def preorderTraversal(self,root:TreeNode)->List[int]:
		if not root:
			return []
		res=[]
		stack=[root]
		while stack:
			cur=stack.pop()
			res.append(cur.val)
			if cur.right:
				stack.append(cur.right)
			if cur.left:
				stack.append(cur.left)
		return res

这个方法我需要评论一下,这个压栈操作非常的amazing,同样的,也是先给stack存一个val,然后进循环以后,就抛一个val出来给res,append,cur.val就是数据,然后本来应该是先左后右的,但是栈是放盘子,所以抛出的时候是反着来的,所以就先右入后左入,抛的时候就是正确的顺序
最后return res
专业术语叫,先将右子节点压栈,然后是左子结点,确保每次处理都是先根再左然后右,这样的好处是,与递归方法相比,迭代法避免了函数调用的开销,适合处理深度较大的树,避免了递归深度过大导致栈溢出的问题。
后序遍历代码如下:
后序遍历代码不写了,没意义,就是把前序代码镜像一下,然后倒序输出,注意倒序输出就是res[::-1]

**方法四:**前中后序通用模板,主要是为了解决中序遍历没有简单办法的说法,中序都能写了,那前序后序就是随便写

class Solution:
    def inorderTraversal(self, root: TreeNode) -> List[int]: 
        res = []
        stack = []
        cur = root
        # 中序,模板:先用指针找到每颗子树的最左下角,然后进行进出栈操作
        while stack or cur:
            while cur:
                stack.append(cur)
                cur = cur.left
            cur = stack.pop()
            res.append(cur.val)
            cur = cur.right
        return res
        
        # # 前序,相同模板
        # while stack or cur:
        #     while cur:
        #         res.append(cur.val)
        #         stack.append(cur)
        #         cur = cur.left
        #     cur = stack.pop()
        #     cur = cur.right
        # return res
        
        # # 后序,相同模板
        # while stack or cur:
        #     while cur:
        #         res.append(cur.val)
        #         stack.append(cur)
        #         cur = cur.right
        #     cur = stack.pop()
        #     cur = cur.left
        # return res[::-1]

N叉树简洁递归:

class Solution:
	def preorder(self,root:'Node')->List[int]:
		if not root: return []
		res=[root.val]
		for node in root.children:
			res.extend(self.preorder(node))
		return res

N叉树通用递归

class Solution:
    def preorder(self, root: 'Node') -> List[int]:
        res = []
        def helper(root):
            if not root:
                return
            res.append(root.val)
            for child in root.children:
                helper(child)
        helper(root)
        return res

N叉树迭代

class Solution:
    def preorder(self, root: 'Node') -> List[int]:
        if not root:
            return []
        s = [root]
        # s.append(root)
        res = []
        while s:
            node = s.pop()
            res.append(node.val)
            # for child in node.children[::-1]:
            #     s.append(child)
            s.extend(node.children[::-1])
        return res

7.翻转二叉树

class Solution:
	def invertTree(self,root:Optional[TreeNode])->Optional[TreeNode]:
		if not root:
			return root

		left=self.invertTree(root.left)
		right=self.invertTree(root.right)
		root.left,root.right=right,left
		return root
  • 20
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值