这三种遍历其实都是dfs,只是递归的位置不同,一般二叉树用到dfs的都是这种区别(递归在前还是再后)
这个题就是典型的前序遍历思想了:
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
//当且仅当递归的底层会成立:此出root理解为:最下面的p或q节点,然后回溯
if(root == null || root == p || root == q){return root;}
/**当不是最底层,就 前序:先深入到最底层 */
//当前的左节点是否能匹配上p或q,匹配不上返回null
TreeNode left = lowestCommonAncestor(root.left, p, q);
//当前的右节点是否能匹配上p或q,匹配不上返回null
TreeNode right = lowestCommonAncestor(root.right, p, q);
/**此时的left和right,如果匹配不上就是null(参考第一行代码),匹配上了因为回溯会返回p或q */
/**此时考虑场景:最顶层、接近底层 一共三个场景*/
//左右都找到了匹配:那最近父节点一定是这个root节点
if(left != null && right != null){return root;}
/**左边or右边匹配:回溯的是p或q的其中一个,
回溯到上面两句left和right那里
例如:left==p,right==q。但是判断时只需要判断左右不为空,那返回root
跟回溯究竟返回的是谁没关系,只要不是null就是匹配到了
*/
if(right == null){return left;}
if(left == null){return right;}
return root;
}
}
普通的二叉树与 二叉搜索树的区别在于:
因为需要保证“整个子树更小or更大”,所以需要自定义一个dfs方法,形参中携带max和min;
class Solution {
public boolean isValidBST(TreeNode root) {
return dfs(root,Long.MAX_VALUE,Long.MIN_VALUE);
}
public boolean dfs(TreeNode curr , long max , long min){
if(curr == null){return true;}
if(curr.val >= max || curr.val <= min){return false;}
return dfs(curr.left, curr.val, min) && dfs(curr.right, max ,curr.val);
}
}
这个题如果是用前序遍历(DFS)先存储结果到list中,空间复杂度为O(n),还有一种方法不用DFS空间复杂度为O(1)参考解答
既然提到了前序遍历,那直接把前序遍历的结果存储在list中,然后再循环恢复就好
class Solution {
public void flatten(TreeNode root) {
List list = new ArrayList();
preorderTraversal(root,list);
//此时list中有前序遍历的结果了
for(int i = 0 ; i < list.size() - 1 ; i++){
list.get(i).right = list.get(i+1);
list.get(i).left = null;
}
//因为最后一个节点本身left和right都是null,所以不用处理
}
//前序遍历的同时把值存入list
public void preorderTraversal(TreeNode curr,List list){
if(curr == null){return;}
list.add(curr);
preorderTraversal(curr.left,list);
preorderTraversal(curr.right,list);
}
}
一个简单题
class Solution {
public int maxDepth(TreeNode root) {
if(root == null){return 0;}
//左右子树分别递归,最终结果取Max + 1
return Math.max( maxDepth(root.left) , maxDepth(root.right) ) + 1;
}
}
给你两棵二叉树: root1 和 root2 。
想象一下,当你将其中一棵覆盖到另一棵之上时,两棵树上的一些节点将会重叠(而另一些不会)。你需要将这两棵树合并成一棵新二叉树。合并的规则是:如果两个节点重叠,那么将这两个节点的值相加作为合并后节点的新值;否则,不为 null 的节点将直接作为新二叉树的节点。
返回合并后的二叉树。
注意: 合并过程必须从两个树的根节点开始。
class Solution {
public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
if(root1 == null && root2 == null){return null;}
int v1 = root1 != null ? root1.val : 0 ;
int v2 = root2 != null ? root2.val : 0 ;
TreeNode curr = new TreeNode(v1+v2);
//这里面还要写三目运算符主要是排除空指针问题
curr.left = mergeTrees(root1 != null ? root1.left : null, root2 != null ? root2.left : null);
curr.right = mergeTrees(root1 != null ? root1.right : null, root2 != null ? root2.right : null);
return curr;
}
}
class Solution {
public TreeNode invertTree(TreeNode root) {
TreeNode res = root;
reverse(root);
return res;
}
public void reverse(TreeNode curr){
if(curr == null) return;
TreeNode temp = curr.left;
curr.left = curr.right;
curr.right = temp;
reverse(curr.left);
reverse(curr.right);
}
}
class Solution {
public boolean isSymmetric(TreeNode root) {
if(root == null){return false;}
return check(root.left, root.right);
}
public boolean check(TreeNode left , TreeNode right){
if(left == null && right == null){return true;}
if(left == null || right == null){return false;}
if(left.val != right.val){return false;}
//这里一定是左左 比 右右 && 右右 比 左左
return check(left.left, right.right) && check(right.left, left.right);
}
}
这看起来是一个字符串问题,但实际上可以用二叉树dfs,因为很明显**()可以分别对应左子树、右子树**
public class Solution {
// 做减法
public List generateParenthesis(int n) {
List res = new ArrayList<>();
// 特判
if (n == 0) {
return res;
}
// 执行深度优先遍历,搜索可能的结果
dfs("", n, n, res);
return res;
}
/**
* @param curStr 当前递归得到的结果
* @param left 左括号还有几个可以使用
* @param right 右括号还有几个可以使用
* @param res 结果集
*/
private void dfs(String curStr, int left, int right, List res) {
// 因为每一次尝试,都使用新的字符串变量,所以无需回溯
// 在递归终止的时候,直接把它添加到结果集即可,注意与「力扣」第 46 题、第 39 题区分
if (left == 0 && right == 0) {
res.add(curStr);
return;
}
// 剪枝(如图,左括号可以使用的个数严格大于右括号可以使用的个数,才剪枝,注意这个细节)
if (left > right) {
return;
}
if (left > 0) {
dfs(curStr + "(", left - 1, right, res);
}
if (right > 0) {
dfs(curStr + ")", left, right - 1, res);
}
}
}