代码随想录 刷题记录-10 二叉树(4)习题

1.617.合并二叉树

递归

以先序遍历为例。

按照递归三部曲来解决:

1、确定递归函数的参数和返回值:

首先要合入两个二叉树,那么参数至少是要传入两个二叉树的根节点,返回值就是合并之后二叉树的根节点。

代码如下:

TreeNode* mergeTrees(TreeNode* t1, TreeNode* t2) {

2、确定终止条件:

因为是传入了两个树,那么就有两个树遍历的节点t1 和 t2,如果t1 == NULL 了,两个树合并就应该是 t2 了(如果t2也为NULL也无所谓,合并之后就是NULL)。

反过来如果t2 == NULL,那么两个数合并就是t1(如果t1也为NULL也无所谓,合并之后就是NULL)。

代码如下:

if (t1 == NULL) return t2; // 如果t1为空,合并之后就应该是t2
if (t2 == NULL) return t1; // 如果t2为空,合并之后就应该是t1

3、确定单层递归的逻辑:

单层递归的逻辑就比较好写了,这里我们重复利用一下t1这个树,t1就是合并之后树的根节点(就是修改了原来树的结构)。

那么单层递归中,就要把两棵树的元素加到一起。

t1->val += t2->val;

接下来t1 的左子树是:合并 t1左子树 t2左子树之后的左子树。

t1 的右子树:是 合并 t1右子树 t2右子树之后的右子树。

最终t1就是合并之后的根节点。

代码如下:

t1->left = mergeTrees(t1->left, t2->left);
t1->right = mergeTrees(t1->right, t2->right);
return t1;

此时前序遍历,完整代码就写出来了,如下:

class Solution {
public:
    TreeNode* mergeTrees(TreeNode* t1, TreeNode* t2) {
        if (t1 == NULL) return t2; // 如果t1为空,合并之后就应该是t2
        if (t2 == NULL) return t1; // 如果t2为空,合并之后就应该是t1
        // 修改了t1的数值和结构
        t1->val += t2->val;                             // 中
        t1->left = mergeTrees(t1->left, t2->left);      // 左
        t1->right = mergeTrees(t1->right, t2->right);   // 右
        return t1;
    }
};

Java 代码:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
        if(root1 == null) return root2;
        if(root2 == null) return root1;
        root1.val = root1.val + root2.val;
        root1.left = mergeTrees(root1.left, root2.left);
        root1.right = mergeTrees(root1.right, root2.right);
        return root1;
    }
}

迭代

class Solution {
    // 使用队列迭代
    public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
        if (root1 == null) return root2;
        if (root2 ==null) return root1;
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root1);
        queue.offer(root2);
        while (!queue.isEmpty()) {
            TreeNode node1 = queue.poll();
            TreeNode node2 = queue.poll();
            // 此时两个节点一定不为空,val相加
            node1.val = node1.val + node2.val;
            // 如果两棵树左节点都不为空,加入队列
            if (node1.left != null && node2.left != null) {
                queue.offer(node1.left);
                queue.offer(node2.left);
            }
            // 如果两棵树右节点都不为空,加入队列
            if (node1.right != null && node2.right != null) {
                queue.offer(node1.right);
                queue.offer(node2.right);
            }
            // 若node1的左节点为空,直接赋值
            if (node1.left == null && node2.left != null) {
                node1.left = node2.left;
            }
            // 若node1的右节点为空,直接赋值
            if (node1.right == null && node2.right != null) {
                node1.right = node2.right;
            }
        }
        return root1;
    }
}

2.700.二叉搜索树中的搜索

二叉搜索树是一个有序树:

  • 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
  • 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
  • 它的左、右子树也分别为二叉搜索树

递归法:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public TreeNode searchBST(TreeNode root, int val) {
        if(root == null || root.val == val) return root;
        if(root.val > val) return searchBST(root.left,val);
        else return searchBST(root.right,val);
    }
}

迭代法:

由于二叉搜索树的有序性,不需要回溯,按照其顺序性搜索即可。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public TreeNode searchBST(TreeNode root, int val) {
        while(root != null){
            if(root.val == val) break;
            if(root.val > val) root = root.left;
            else root = root.right;
        }
        return root;
    }
}

3.98.验证二叉搜索树

中序遍历下,输出的二叉搜索树节点的数值是有序序列。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    Deque<Integer> queue = new ArrayDeque<>();
    public boolean isValidBST(TreeNode root) {
        inOrder(root);
        Integer before = queue.remove();
        while(!queue.isEmpty()){
            Integer now = queue.remove();
            if(before >= now) return false;
            before = now;
        }
        
        return true;
    }
    
    public void inOrder(TreeNode root){
        if(root == null) return;
        if(root.left!=null) inOrder(root.left);
        queue.add(root.val);
        if(root.right!=null) inOrder(root.right);
    }
}

在递归中直接比较:

1.考虑参数和返回值:

        因为可能出现比较一半直接判定false,所以需要返回值,返回值类型是boolean;

        参数是TreeNode root;

        (用全局变量记录preNode)

2.终止条件

        (1)null ; return true

        (2) preNode.val >= root.val ; return false

3.每一层的逻辑

        左中右 进行 中序遍历

        注意在中的位置 更新preNode

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    TreeNode preNode;
    public boolean isValidBST(TreeNode root) {
        if(root == null) return true;
        boolean left = isValidBST(root.left);
        if(preNode!=null && preNode.val >= root.val) return false;
        preNode = root;
        boolean right = isValidBST(root.right);
        return left && right;
    }
    

}

4.530.二叉搜索树的最小绝对差

Deque<Integer> queue = new ArrayDeque<>();
    public int getMinimumDifference(TreeNode root) {
        inOrder(root);
        Integer pre = queue.remove();
        Integer min = Integer.MAX_VALUE;
        while(!queue.isEmpty()){
            Integer now = queue.remove();
            if(min > Math.abs(now - pre)){
                min=Math.abs(now - pre);
            }
            pre = now;
        }
        
        return min;
    }
    
    public void inOrder(TreeNode root){
        if(root == null) return;
        inOrder(root.left);
        queue.add(root.val);
        inOrder(root.right);
    }

遇到在二叉搜索树上求什么最值,求差值之类的,都要思考一下二叉搜索树可是有序的,要利用好这一特点。

5.501.二叉搜索树中的众数

一种比较麻烦,空间复杂度O(n)的写法,这种方法无论是不是二叉搜索树都可以,没有利用二叉搜索树的性质:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    Map<Integer,Integer> map = new HashMap<>();
    public int[] findMode(TreeNode root) {
        preOrder(root);
        Integer max = 0;
        for(Map.Entry<Integer,Integer> item : map.entrySet()){
            if(max < item.getValue()) max = item.getValue();
        }
        List<Integer> res = new ArrayList<>();
        for(Map.Entry<Integer,Integer> item :map.entrySet()){
            if(item.getValue() == max) res.add(item.getKey());
        }
        
        int[] resArray = new int[res.size()];
        for(int i=0; i<res.size() ;i++){
            resArray[i]=res.get(i);
        }
        
        return resArray;
    }

    public void preOrder(TreeNode root){
        if(root == null) return;
        map.put(root.val,map.getOrDefault(root.val,0)+1);
        preOrder(root.left);
        preOrder(root.right);
    }
}

使用O(1)的空间复杂度解决:

在递归过程中记录

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {

    int count;
    int maxCount;
    List<Integer> res;
    TreeNode pre;

    public int[] findMode(TreeNode root) {
        count = 0;
        maxCount = 0;
        res = new ArrayList<>();
        pre = null;

        inOrder(root);

        int[] resArray =  new int[res.size()];
        for(int i=0; i<res.size() ; i++){
            resArray[i] = res.get(i);
        }

        return resArray;
    }

    public void inOrder(TreeNode root){
        if(root == null) return;
        inOrder(root.left);
        int rootValue = root.val;
        if(pre == null || rootValue == pre.val){
            count++;
        }else{
            count = 1;
        }
        pre = root;
        if(count == maxCount){
            res.add(rootValue);
        }else if(count > maxCount){
            maxCount = count;
            res.clear();
            res.add(rootValue);
        }
        inOrder(root.right);

    }

   
}

6.236. 二叉树的最近公共祖先

思路

遇到这个题目首先想的是要是能自底向上查找就好了,这样就可以找到公共祖先了。

那么二叉树如何可以自底向上查找呢?

回溯啊,二叉树回溯的过程就是从低到上。

后序遍历(左右中)就是天然的回溯过程,可以根据左右子树的返回值,来处理中节点的逻辑。

判断逻辑是 如果递归遍历遇到q,就将q返回,遇到p 就将p返回,那么如果 左右子树的返回值都不为空,说明此时的中节点,一定是q 和p 的最近祖先。并且这种判断逻辑包含了p是q的祖先结点或者q是p的祖先结点的特殊情况。

递归三部曲:

  • 确定递归函数返回值以及参数

需要递归函数返回值,来告诉我们是否找到节点q或者p,那么返回值为bool类型就可以了。

但我们还要返回最近公共节点,可以利用上题目中返回值是TreeNode * ,那么如果遇到p或者q,就把q或者p返回,返回值不为空,就说明找到了q或者p。

TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q)
  • 确定终止条件

遇到空的话,因为树都是空了,所以返回空。

如果 root == q,或者 root == p,说明找到 q p ,则将其返回,这个返回值,后面在中节点的处理过程中会用到,那么中节点的处理逻辑,下面讲解。

if (root == q || root == p || root == NULL) return root;
  • 确定单层递归逻辑

本题函数有返回值,是因为回溯的过程需要递归函数的返回值做判断。本题需要遍历整棵树而不是部分树(一条边)

搜索一条边的写法:

if (递归函数(root->left)) return ;

if (递归函数(root->right)) return ;

搜索整个树写法:

left = 递归函数(root->left);  // 左
right = 递归函数(root->right); // 右
left与right的逻辑处理;         // 中 

在递归函数有返回值的情况下:如果要搜索一条边,递归函数返回值不为空的时候,立刻返回,如果搜索整个树,直接用一个变量left、right接住返回值,这个left、right后序还有逻辑处理的需要,也就是后序遍历中处理中间节点的逻辑(也是回溯)

TreeNode* left = lowestCommonAncestor(root->left, p, q);
TreeNode* right = lowestCommonAncestor(root->right, p, q);
if (left == NULL && right != NULL) return right;
else if (left != NULL && right == NULL) return left;
else  { //  (left == NULL && right == NULL)
    return NULL;
}

总结

这道题目刷过的同学未必真正了解这里面回溯的过程,以及结果是如何一层一层传上去的。

那么我给大家归纳如下三点

  1. 求最小公共祖先,需要从底向上遍历,那么二叉树,只能通过后序遍历(即:回溯)实现从底向上的遍历方式。

  2. 在回溯的过程中,必然要遍历整棵二叉树,即使已经找到结果了,依然要把其他节点遍历完,因为要使用递归函数的返回值(也就是代码中的left和right)做逻辑判断

  3. 要理解如果返回值left为空,right不为空为什么要返回right,为什么可以用返回right传给上一层结果。

可以说这里每一步,都是有难度的,都需要对二叉树,递归和回溯有一定的理解。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root == null || root == p || root == q) return root;
        TreeNode left = lowestCommonAncestor(root.left,p,q);
        TreeNode right = lowestCommonAncestor(root.right,p,q);
        if(left != null && right != null) return root;
        else if(left != null) return left;
        else return right;
    }
}

一层的逻辑如果写具体一些,是这样的:

        if(left != null && right != null) return root;
        else if(left==null && right !=null) return right;
        else if (left!=null && right == null) return left;
        else return null;

本节总结参考:本周小结!(二叉树系列四)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值