二叉树展开为链表
我的解答:
List<TreeNode> list = new ArrayList<>();
public void flatten(TreeNode root) {
if(root == null){
return;
}
getList(root);
for(int i=1; i<list.size(); i++){
list.get(i-1).left = null;
list.get(i-1).right = list.get(i);
}
}
public void getList(TreeNode node){
if(node == null){
return;
}
list.add(node);
getList(node.left);
getList(node.right);
}
分析:代码的时间复杂度为O(n),空间复杂度为O(n)。自己未能想出时间复杂度为O(n)且空间复杂度为O(1)的解法。解题思路:用递归对二叉树进行前序遍历,用集合存储二叉树先序遍历的结果,然后遍历集合更改指针关系即可。
看了官方题解后的解答:
//方法一:前序遍历(递归版)(与我的解答一致)
//时间复杂度:O(n)
//空间复杂度:O(n)
//方法一:前序遍历(迭代版)
//时间复杂度:O(n)
//空间复杂度:O(n)
public void flatten(TreeNode root) {
Deque<TreeNode> stack = new LinkedList<>();
List<TreeNode> list = new ArrayList<>();
TreeNode node = root;
while(node != null || !stack.isEmpty()){
while(node != null){
list.add(node);
stack.push(node);
node = node.left;
}
node = stack.pop().right;
}
for(int i = 1; i < list.size(); i++){
list.get(i-1).right = list.get(i);
list.get(i-1).left = null;
}
}
//方法二(只看思路自己实现版):前序遍历和展开同步进行
//时间复杂度:O(n)
//空间复杂度:O(n)
public void flatten(TreeNode root) {
if(root == null){
return;
}
Deque<TreeNode> stack = new LinkedList<>();
TreeNode cur = root, pre = null;
while(cur != null || !stack.isEmpty()){
while(cur != null){
if(cur.right != null){
stack.push(cur.right);
}
if(pre != null){
pre.right = cur;
pre.left = null;
}
pre = cur;
cur = cur.left;
}
if(!stack.isEmpty()){
cur = stack.pop();
}
}
}
//方法二(看了官方实现版,优于只看思路自己实现版):前序遍历和展开同步进行
//时间复杂度:O(n)
//空间复杂度:O(n)
public void flatten(TreeNode root) {
if(root == null){
return;
}
Deque<TreeNode> stack = new LinkedList<>();
TreeNode pre = null, cur;
stack.push(root);
while(!stack.isEmpty()){
cur = stack.pop();
if(pre != null){
pre.right = cur;
pre.left = null;
}
if(cur.right != null){
stack.push(cur.right);
}
if(cur.left != null){
stack.push(cur.left);
}
pre = cur;
}
}
//方法三:寻找前驱节点
//时间复杂度:O(n)
//空间复杂度:O(1)
public void flatten(TreeNode root) {
TreeNode cur = root, right;
while(cur != null){
if(cur.left != null){
TreeNode temp = cur.left;
while(temp.right != null){
temp = temp.right;
}
temp.right = cur.right;
cur.right = cur.left;
cur.left = null;
}
cur = cur.right;
}
}
分析:
1、方法一和方法二都采用二叉树的前序遍历方法,区别在于,方法一是将前序遍历的结果存入集合中,遍历完后再遍历集合更改指针关系;而方法二利用栈保存了每个节点的右孩子信息,每次右孩子先入栈,左孩子后入栈,且用pre保存当前已经展开的链表尾节点,每次出栈的同时更改pre的指针关系。重复以上操作直到栈为空。
2、方法三的解题思路:遍历到当前节点时分为两种情况------若当前节点左孩子为空,则无需做任何改变;若当前节点左孩子不为空,根据前序遍历的顺序关系,每次找到当前节点的左子树前序遍历的最后一个节点(最右节点),将当前节点的右子树连接到最右节点,然后让当前节点的左孩子成为右孩子,并让左孩子为空,最后继续遍历下一个节点。重复以上操作直到当前节点为空。
总结
- 本题主要是需要掌握二叉树的前序遍历的流程和特点。