LeetCode50天刷题计划第二季(Day 12 — 路径总和 II(12.00-12.40)二叉树展开为链表(16.40.-18.10)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

国庆假期结束!开始工作!

一、题目

路径总和 II

给你二叉树的根节点 root 和一个整数目标和 targetSum ,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。

叶子节点 是指没有子节点的节点。

示例

示例 1:
输入:root = [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum = 22
输出:[[5,4,11,2],[5,8,4,5]]

示例 2:
输入:root = [1,2,3], targetSum = 5
输出:[]

示例 3:
输入:root = [1,2], targetSum = 0
输出:[]

提示

树中节点总数在范围 [0, 5000] 内
-1000 <= Node.val <= 1000
-1000 <= targetSum <= 1000

二、思路

一眼dfs 但是对于终止条件和结束条件的判断需要结合题意格外注意:
注意,必须是根结点到叶子结点,也就是说完成状态必须是root的左右孩子均为None,且数值成立的情况
注意,值有可能为负值,所以无法判断结束状态

三、代码

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def pathSum(self, root: Optional[TreeNode], targetSum: int) -> List[List[int]]:
        result=[]
        def dfs(root,path,currentSum):
            if(root.left ==None and root.right==None and currentSum == targetSum): #终止状态
                result.append(path[:])
                return
            #递归+回溯
            if(root.left): #左孩子
                path.append(root.left.val)
                dfs(root.left,path,currentSum+root.left.val)
                path.pop()
            if(root.right): #右孩子
                path.append(root.right.val)
                dfs(root.right,path,currentSum+root.right.val)
                path.pop()
            return
        #根结点存在
        if(root !=None ):
            dfs(root,[root.val],root.val)
            return result
        else:
            return []
        

四、题目

二叉树展开为链表

给你二叉树的根结点 root ,请你将它展开为一个单链表:
展开后的单链表应该同样使用 TreeNode ,其中 right 子指针指向链表中下一个结点,而左子指针始终为 null 。
展开后的单链表应该与二叉树 先序遍历 顺序相同。

示例

示例 1:
输入:root = [1,2,5,3,4,null,6]
输出:[1,null,2,null,3,null,4,null,5,null,6]

示例 2:
输入:root = []
输出:[]

示例 3:
输入:root = [0]
输出:[0]

提示

树中结点数在范围 [0, 2000] 内
-100 <= Node.val <= 100

进阶

你可以使用原地算法(O(1) 额外空间)展开这棵树吗?

五、思路

1、空间复杂度O(n)

先对它进行先序遍历,每次记录遍历中的上一个结点~但前序遍历需要用递归实现,也就是需要消耗栈的空间,空间复杂度并不是O(1)
第一步,转换为左节点的单链表,以防止数据丢失 ; 第二步,转换为右节点

2、空间复杂度O(1)

但如果想要O(1)空间复杂度,就需要精准找到每个结点的右子树的前驱结点。
首先,对于每一个结点A来说,因为我们的目的是把目标元素挪到A的右孩子位置,因此要先把A的右孩子的位置空出来。结点A右孩子(也就是右子树的根结点)的前一个元素一定是结点A左子树的最右节点(root.right到最后的结点),因此对A的左子树进行遍历找到这个结点,并把A的右子树接在其后,然后此时A的右孩子已经空出,可以把左孩子接上了。再看下一个结点,也就是A的右孩子,有无左孩子,如果有的话仍按前面的操作一遍即可,直至所有节点都没左孩子。

六、代码

1、空间复杂度O(n)

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def flatten(self, root: Optional[TreeNode]) -> None:
        """
        Do not return anything, modify root in-place instead.
        """
        last_root=[TreeNode()] 
        def root_first_traval(root): 
            if(root == None): #空
                return
            last_root[0].left=root #操作
            last_root[0]=root #更改上一个结点
            root_first_traval(root.left)
            root_first_traval(root.right)
        root_first_traval(root)
  
        while(root != None):
            root.right=root.left
            root.left=None
            root=root.right
        return root
            

2.空间复杂度O(1)

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def flatten(self, root: Optional[TreeNode]) -> None:
        """
        Do not return anything, modify root in-place instead.
        """
        def find(root):
            while(root.right): #先找到最右子树
                root=root.right
            return root
        while(root !=None):
            if(root.left != None): #有左子树
                find(root.left).right=root.right #右孩子空出来
                root.right=root.left #左孩子接上去
                root.left=None #左孩子删去
            root=root.right
        return root
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值