输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的循环双向链表。要求不能创建任何新的节点,只能调整树中节点指针的指向。
为了让您更好地理解问题,以下面的二叉搜索树为例:
我们希望将这个二叉搜索树转化为双向循环链表。链表中的每个节点都有一个前驱和后继指针。对于双向循环链表,第一个节点的前驱是最后一个节点,最后一个节点的后继是第一个节点。
下图展示了上面的二叉搜索树转化成的链表。“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]