114. 二叉树展开为链表
给你二叉树的根结点 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)
额外空间)展开这棵树吗?
题解
题目既然是说使用原地算法(O(1)
额外空间),那么先展开记录节点,然后再按要求建树开销就不止O(1)
了,
要实现O(1)
就不能额外建树。经过观察发现,展开之后的二叉树其实是先将右子树按先序遍历展开得到链表形式的树R
,再将左子树按先序遍历展开树L
,
然后拼接成root-L-R
,如root = [1,2,5,3,4,null,6]
,先得到树R:[5,null,6]
,再得到树L:[2,null,3,null,4]
,拼接起来是[1,null,2,null,3,null,4,null,5,null,6]
,
那么先编写一个函数将一棵树以链表形式展开,然后依次放入根节点的左右子树,再拼接就可以完成了。
为了节约空间复杂度,将树展开时就同时完成拼接的工作,就不用将这棵树再多存储一次了。
首先题目要求的先序遍历,根据递归写出基本代码,然后因为要同时修改节点之间的关系来完成拼接,那么就传入root
节点,
void work(TreeNode* &p,TreeNode* &root) {//传入当前节点,根节点
if(p==nullptr) { return; }
work(p->right,root);
work(p->left,root);
//重新拼接 }
接下来解决拼接的部分,需要将p
节点拼接到root
右子树的位置上,而原右子树的位置的节点则变成p
的右子树,如root-right,p=>root-p-right
,
那么拼接部分应该为:p->right=root->right; p->left=nullptr; root->right=p;
先令p
指向root
指向的右节点,然后将root
指向更改为p
,注意将p
的左子树赋值为空,
将代码与思路串联起来,先取出根节点右子树(额外空间1来存储),与根节点一起传入work
函数,
再取出根节点左子树,同样传入函数,完成拼接。
class Solution {
public:
void work(TreeNode* &p,TreeNode* &root) {
if(p==nullptr)
{
return;
}
work(p->right,root);
work(p->left,root);
p->right=root->right;
p->left=nullptr;
root->right=p;
}
void flatten(TreeNode* root) {
if(root!=nullptr)
{
TreeNode *result=root->right;
root->right=nullptr;
work(result,root);
result=root->left;
root->left=nullptr;
work(result,root);
}
}
};