java数据结构_二叉树的相关面试题_5.6

1. 检查两颗树是否相同。

力扣链接:100. 相同的树 - 力扣(LeetCode)

首先要理解题意:相同指的是两棵树的 结构 和 数值 均是相同的。

如下图:p 和 q两棵树 结构和其每个结点的数值均相同,才是两棵相同的树

如下图:q这棵树的结构,与p对比来讲,少了根为1的左结点,则不是两棵相同的树

如下图:q的根结点的值与p不同,则不是两棵相同的树

总结下来:不相同有两种情况:

1.一棵树的某棵子树为空,另一棵树的对应的那棵子树不为空,如下图:

2.虽然两棵树都不为空,但其值不相同

所以判断两棵树是否相同,也是需要对两棵树分别进行遍历,考虑到前序遍历根先后的顺序比较方法,于是有如下代码:

public boolean isSameTree(TreeNode p, TreeNode q) {

    //如果两棵树中,一棵为空,另一课非空,则一定不相同
    if((p != null && q == null) || (p == null && q != null)) {
        return false;
    }
    
    //如果两棵树均为空,则相同
    if(p == null && q == null) {   
        return true;
    }

    //代码执行到这里,两棵树一定都不为null
    if(p.val != q.val) {
        return false;
    }
    
    return isSameTree(p.left, q.left) && isSameTree(p.right, q.right);
}

注意代码的顺序一定要检查是否为空,然后再对于检查p.val和q.val是否相同。

还需要注意的是,最后判断的代码一定要判断是否不相同,反会false,不能判断是否相同,然后直接返回true,如果直接判断当前结点是否相同,然后直接返回true,就无法再向下递归树的左右子树是否相同了!

同样的,该代码的遍历过程仍可以像上篇文章一样模拟出来,此处省略...

补充,如果p树的结点个数为p,q树的结点个数为q,则该方法的时间复杂度为min(p,q)

2. 另一颗树的子树。

力扣链接:572. 另一棵树的子树 - 力扣(LeetCode)

首先要明白题意,给root树的根结点root,subRoot树的根结点subRoot,判断在root中,是否有subRoot结构。

如下图,在root树中有subRoot结构

如下图,在root树中则不包含subRoot树(多了一个0)

还有一种情况,如下图,root和subRoot相同,则也算在root树中包含subRoot,这样判断的话,不就是第一道题中的判断两树是否相同

如果最开始的root和sbuRoot不相同的话,则向下递归,看root的左子树和右子树和subRoot是否相同,这样就出现了递归的操作。

于是有如下代码:

因为是递归方法,所以肯定会递归的某棵树的子树为空,如果继续进行子树的子树进行递归,会出现空指针异常,所以要先判断root == null,防止空指针异常。然后需要使用第一道题的isSameTree方法,来判断当前结点为root的树是否和subRoot树相同。

注意,这样必须是直接判断两棵树是否相同,不可以 if(!isSameTree(root, subRoot)) return false; 不能直接当前树不包含subRoot就直接返回false,万一当前树的子树包含呢?

之后判断左子树和右子树递归是否包含subRoot,如果都没有返回true,则返回false

 完整代码为:

class Solution {
        public boolean isSameTree(TreeNode p, TreeNode q) {

        // 如果两棵树中,一棵为空,另一课非空,则一定不相同
        if ((p != null && q == null) || (p == null && q != null)) {
            return false;
        }

        // 如果两棵树均为空,则相同
        if (p == null && q == null) {
            return true;
        }

        // 代码执行到这里,两棵树一定都不为null
        if (p.val != q.val) {
            return false;
        }

        return isSameTree(p.left, q.left) && isSameTree(p.right, q.right);
    }

    public boolean isSubtree(TreeNode root, TreeNode subRoot) {
        
        if(root == null) {
            return false;
        }

        if(isSameTree(root, subRoot)) {
            return true;
        }

        if(isSubtree(root.left, subRoot)) {
            return true;
        }

        if(isSubtree(root.right, subRoot)) {
            return true;
        }

        return false;
    }
}

补充:假设 root树的结点的个数为 r,subRoot树的结点个数为s,则该方法的时间复杂度为        O(r * s),因为,root的每一个结点都要遍历一下,看是否与subRoot树相同

3.反转二叉树

力扣链接:226. 翻转二叉树 - 力扣(LeetCode)

如下图,讲二叉树的每一个结点的左右子树都进行互换,注意,在进行交换的适合,是要将左右子树的对应的引用进行交换,而不是仅仅将引用中的值进行交换。

可以想到,需要创建一个中间变量tmp,tmp = 地址A,地址A = 地址B,地址B = tmp,于是有代码如下:

public TreeNode invertTree(TreeNode root) {
    if(root == null) return null;

    TreeNode tmp = root.left;
    root.left = root.right;
    root.right = tmp;

    invertTree(root.left);
    invertTree(root.right);

    return root;
}

 4. 平衡二叉树

力扣链接:110. 平衡二叉树 - 力扣(LeetCode)

平衡二叉树:每个节点的左子树和右子树的高度差的绝对值不超过 1,并且左右子树也都是平衡二叉树。

如下图:3的左子树高度为1,右子树高度为2,相差为1,9的左右子树均为0,相差为0,20的左右子树高度均为1,相差为0,15的左右子树均为0,相差为0,7的左右子树均为0,相差为0。所以该二叉树为平衡二叉树。

如下图,3的左子树的高度为4,右子树的高度为4,相差为0。9的左子树高度为2,右子树高度为0,相差为2,只要存在高度差的绝对值大于1的情况,则这棵二叉树就不是平衡二叉树。

所以,要实现该方法,纪要保证当前结点的左右节点的高度差小于1,也要保证该结点的左树是平衡的(递归)也要保证该结点的右树是平衡的(递归)。

求树的高度差的方法也在前面有所讲述,即getHeight方法。

于是有代码:

public int getHeight(TreeNode root) {
    if(root == null) return 0;

    int leftHeight = getHeight(root.left);
    int rightHeight = getHeight(root.right);

    return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}

public boolean isBalanced(TreeNode root) {
    if(root == null) return true;

    int leftHeight = getHeight(root.left);
    int rightHeight = getHeight(root.right);

    return Math.abs(leftHeight - rightHeight) <= 1 && isBalanced(root.left)               
    && isBalanced(root.right);
}

将上述代码在力扣中可以正常通过检查,但要考虑一个问题,上述代码是否做了很多的冗余计算,例如下面这幅图,在getHeight方法中,要遍历一次整个二叉树,在isBalanced的递归中,又要遍历一次二叉树,遍历一次二叉树的时间复杂度为O(n),这样就使得该方法的时间复杂度为O(n^2)。

仔细观察,在获取高度的适合,遍历到根结点为9的子树的时候,其9为根结点的左子树的高度为2,右子树的高度为0,已经发现了不符合平衡二叉树的定义,就不用再在isBalanced方法中遍历这里了。

可以在求高度的过程当中,就知道,这棵树是否平衡。

于是有如下代码:

public int getHeight(TreeNode root) {
    if(root == null) return 0;

    int leftHeight = getHeight(root.left);
    int rightHeight = getHeight(root.right);

    if(leftHeight >= 0 && rightHeight >= 0 &&
    Math.abs(leftHeight - rightHeight) <= 1) {
        return Math.max(leftHeight,rightHeight) + 1;
    } else {
        return -1;        
    }
}

public boolean isBalanced(TreeNode root) {
    if(root == null) return true;

    return getHeight(root) > 0;
}

这样就可以在求高度的过程当中,检查这棵树是否平衡,如果是平衡,正常返回高度,如果不平衡直接返回-1,在isBalanced方法中,直接以是否大于0做检查即可判断!

5. 对称二叉树

力扣链接:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值