LeetcodeMedium-【面试题36. 二叉搜索树与双向链表】-中序遍历

输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的循环双向链表。要求不能创建任何新的节点,只能调整树中节点指针的指向。

为了让您更好地理解问题,以下面的二叉搜索树为例:
在这里插入图片描述
我们希望将这个二叉搜索树转化为双向循环链表。链表中的每个节点都有一个前驱和后继指针。对于双向循环链表,第一个节点的前驱是最后一个节点,最后一个节点的后继是第一个节点。

下图展示了上面的二叉搜索树转化成的链表。“head” 表示指向链表中有最小元素的节点。
在这里插入图片描述
特别地,我们希望可以就地完成转换操作。当转化完成以后,树中节点的左指针需要指向前驱,树中节点的右指针需要指向后继。还需要返回链表中的第一个节点的指针。

注意:本题与主站 426 题相同:https://leetcode-cn.com/problems/convert-binary-search-tree-to-sorted-doubly-linked-list/

注意:此题对比原题有改动。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/er-cha-sou-suo-shu-yu-shuang-xiang-lian-biao-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路1:先序遍历

直观的可以发现如果不需要链表的化,z中序遍历就是排好序的树了,那么本题的要求转为双向链表,也就是要求在先序遍历的同时修改left、right指针,从而完成双向链表。结合二叉搜索树的性质可以知道,每个节点都大于左子树,小于右子树。故可以知道:
每个结点的需要修改的操作为:
1.left 指向小于自己的最大节点,即左子树中的最右节点;
2.right指向大于自己的最小节点,即右子树中最左节点。

根据这两条其实就可以做题了,需要注意的是遍历函数的设计,应为针对左子树和右子树需要的目标不同,但是制定递归时,并不知道当前遍历的节点是左子树还是右子树,于是我设计的是返回四个参数:分别是左子树的最小值和最大值,右子树的最小值和最大值。这样在调用的地方根据需求取对应的返回值就行了。
还有一点,需要记录头节点和尾节点,最后需要把头尾相连。
在这里插入图片描述

"""
# Definition for a Node.
class Node:
    def __init__(self, val, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right
"""
class Solution:
    def treeToDoublyList(self, root: 'Node') -> 'Node':
        if root == None:
            return None
        self.tail = self.head = None 
        # 先序遍历
        def mid(root): 
            if root == None:
                return None
            rt_l1, rt_r1, rt_l2, rt_r2, rt2_l1, rt2_r1, rt2_l2, rt2_r2, tail = None, None, None, None, None, None, None, None, None
            if root.left:
                rt_l1, rt_r1, rt_l2, rt_r2, _ = mid(root.left)
                # 此节点的left应该指向它的左子树的最右节点,即比它小的最大值                
                root.left = rt_r2
                rt_r2.right = root
            # 记录头节点
            if self.head == None:
                self.head = root
            # print(root.val)
            if root.right:
                rt2_l1, rt2_r1, rt2_l2, rt2_r2, tail  = mid(root.right)
                # 此节点的right应该指向它的右子树的最左节点,即比它大的最小值
                root.right = rt2_l1
                rt2_l1.left = root
            # 需要返回左子树的最右节点,即比自己小的最大值;需要返回右子树的最左节点,即比自己大的最小值; 需要返回尾节点即右子树最右值,即最大值
            return rt_l1 if rt_l1 else root,rt_r2 if rt_r2 else root, rt2_l1 if rt2_l1 else root, rt2_r2 if rt2_r2 else root,  tail if tail else root
        
        _, _,  _, _, self.tail = mid(root)
        # 首位节点相连
        self.head.left = self.tail
        self.tail.right = self.head
        return self.head
    # [-76,null,-6,null,39,null,94]
    # [27,-99,55,null,-34,null,58,null,-8,null,59,null,8,null,68]

思路2:先序,简洁写法

中序遍历
在这里插入图片描述
根据中序输出的结果我们知道就是排序好的顺序,也就是说每次输出的节点它应该链接的节点就是上一个输出的节点,所以只需要用个一个变量记录下上一个输出的节点就行了,然后每次遍历到新输出时,只需要将它与记录的前一个节点进行关联。这个真的简洁啊!!!

"""
# Definition for a Node.
class Node:
    def __init__(self, val, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right
"""
class Solution:
    def treeToDoublyList(self, root: 'Node') -> 'Node':
        if root == None:
            return None
        def dfs(root):
            if root == None:
                return
            dfs(root.left) # 递归左子树
            # print(root.val) 
            if self.head:
                root.left = self.pre # 指向上一个输出的节点
                self.pre.right = root 
            else:
                self.head = root
            self.pre = root # 记录当前输出的节点
            dfs(root.right) # 递归右子树
        self.pre = None
        self.head = None
        dfs(root)
        self.head.left = self.pre
        self.pre.right = self.head
        return self.head
    # [-76,null,-6,null,39,null,94]
    # [27,-99,55,null,-34,null,58,null,-8,null,59,null,8,null,68]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ystraw_ah

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值