114.二叉树展开为链表
问题:给你二叉树的根结点root
,请你将它展开为一个单链表:
- 展开后的单链表应该同样使用
TreeNode
,其中right
子指针指向链表中下一个结点,而左子指针始终为null
。 - 展开后的单链表应该与二叉树
先序遍历
顺序相同。
思路:感觉涉及递归的问题,想起来都很抽象,最主要的明白递归函数的定义。
方法一:递归—后序
比如这道题flatten
函数的定义为:将以root
为根的二叉树转化为链表(拉平)。
明白了定义之后,接下来就是利用这个定义实现将二叉树转化为链表。
- 将
root
的左右子树都拉平(递归函数的工作) - 将
roo
t的右子树接到左子树下面,然后将整个左子树作为root
的右子树。
这样就完成了对以root
为根节点的二叉树的拉平。对于具体如何将root
的左右子树拉平,就是递归函数flatten
的工作,我们只需要考虑,root
的左右子树都已经拉平了,接下来应该怎么操作。
class Solution {
public void flatten(TreeNode root) {
if(root == null) return;
flatten(root.left);
flatten(root.right);
TreeNode left = root.left;
TreeNode right = root.right;
root.left = null;
root.right = left;
TreeNode p = root;
while(p.right != null){
p = p.right;
}
p.right = right;
}
}
方法二:递归----前序
如果在前序遍历时,交换当前节点的左右子树,会出现节点丢失的问题,所以可以先保存当前节点的左右孩子,然后使用头插法的方式建立链表,然后再这样将其左右孩子也转化为链表。
class Solution {
private TreeNode pre = null;
public void flatten(TreeNode root) {
if(root == null) return;
TreeNode l = root.left;
TreeNode r = root.right;
root.left = null;
if(pre != null) {
pre.right = root;
}
pre = root;
flatten(l);
flatten(r);
}
}
方法三:非递归—前序
这道题最主要的一个点在于前序遍历中,当前节点左子树的最右节点为右子树的前驱节点。
根据上面这个思路将这道题转化为寻找当前节点右子树的前驱节点。写法有些类似于Morris算法。对于当前节点cur
有两种情况:
- 当前节点的左孩子为空,此时当前节点右子树的前驱节点就是当前节点,则不做任何操作,继续遍历其右子树
cur=cur.right
。 - 若当前节点的左孩子不为空,此时需要进行以下操作
- 首先寻找其左子树的最右节点作为当前节点
cur
右子树的前驱节点。即mostR.right=cur.right
,mostR表示最右节点; - 然后将当前节点的左子树赋给其右子树,即
cur.right=cur.left
。 - 将当前节点的左子树置为
null
,继续遍历当前节点的左子树,即cur=cur.right
,因为已经将左子树赋给右子树且将左子树置为null
了,此时当前节点的右孩子就是原来的左孩子。
- 首先寻找其左子树的最右节点作为当前节点
- 直到遍历完整颗树。
class Solution {
public void flatten(TreeNode root) {
if(root == null) return;
TreeNode cur = root;
while(cur != null){
if(cur.left != null){
TreeNode mostR = cur.left;
while(mostR.right != null){
mostR = mostR.right;
}
mostR.right = cur.right;
cur.right = cur.left;
cur.left = null;
}
cur = cur.right;
}
}
}