题目:二叉树剪枝
思路分析
首先先判断用前序、中序、后序、层次遍历中的哪一种。
根据题目要求,当要将当前节点剪掉时,其左子树和右子树必须已经被剪掉,所以遍历顺序为左右根的后序遍历最合适。如果用其他遍历方式,根节点是在右子树之前被遍历,不好判断其右子树是否已经被剪掉了,自然也就不能确定当前节点是否需要被剪掉。
非递归后序遍历代码(单栈)
class Solution {
public TreeNode pruneTree(TreeNode root){
if(root == null){
return null;
}
Deque<TreeNode> stack = new ArrayDeque<>();
TreeNode curNode = root;
TreeNode prevNode = null;
while(curNode != null || !stack.isEmpty()){
while(curNode != null){
stack.offerLast(curNode);
curNode = curNode.left;
}
curNode = stack.peekLast();
if(curNode.right != null && curNode.right != prevNode) {
curNode = curNode.right;
} else {
curNode = stack.pollLast();
if(curNode.val == 0 && curNode.left == null && curNode.right == null){
//curNode为栈区变量,直接修改curNode对堆区的树没有影响
//curNode = null;
//tmpNode为栈区变量,tmpNode.left和tmpNode.right为堆区变量,修改left和right会修改树的节点
if(!stack.isEmpty()){
//tmpNode为父节点,curNode为子节点
TreeNode tmpNode = stack.peekLast();
if(tmpNode.left == curNode){
tmpNode.left = null;
} else if(tmpNode.right == curNode){
tmpNode.right = null;
}
} else {
//出栈前栈中只有一个节点,直接返回
return null;
}
}
prevNode = curNode;
curNode = null;
}
}
return root;
}
}
利用栈区遍历修改堆区数据注意
不能直接修改栈区的局部变量,要修改栈区变量指向的堆区空间里面的堆区变量才可以。
如上图,假设tmpNode.left==curNode
,此时将堆区的tmpNode.left
设置为null
,就可以将当前节点剪掉(删除)。
递归后序遍历
class Solution {
public TreeNode pruneTree(TreeNode root) {
if(root == null){
return null;
}
root.left = pruneTree(root.left);
if(root != null){
root.right = pruneTree(root.right);
}
if(root != null && root.val == 0 && root.left == null && root.right == null){
root = null;
}
return root;
}
}
递归
- 递归适合场景:不同规模的相同问题。
- 递归问题思考:没必要纠结递归调用时的子递归的调用细节,应该自顶向下思考递归问题和递归子问题的定义。子递归做的都是相同的事(同样的执行逻辑),随着递归出栈,子问题自底向上的解决,最后解决整个问题。没有思路时可以画递归树。
- 比如树的问题:
- 需要考虑需要返回什么,返回值怎么处理?
- 当前层/当前子树如何处理?
- 需要传递给子递归什么参数?
- 递归的出口,什么时候递归结束?