二叉树OJ题(干货满满)

目录

🐼今日良言:业精于勤而荒于嬉,行成于思而毁于随。

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

🐕2. 另一颗树的子树。

🐂3. 二叉树最大深度。

🐉4. 判断一颗二叉树是否是平衡二叉树。

🐭5. 对称二叉树。

🐷6. 二叉树的构建及遍历。

🐯7. 二叉树的分层遍历 。

🐑8. 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先 。

🐍9. 二叉搜索树转换成排序双向链表。

🐳10. 根据一棵树的前序遍历与中序遍历构造二叉树。

🐇11. 根据一棵树的中序遍历与后序遍历构造二叉树。

🐝12. 二叉树创建字符串。


🐼今日良言:业精于勤而荒于嬉,行成于思而毁于随。

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

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

解题思路:首先判断当前两个结点是否为空,一个为空,一个不为空,说明两棵树不相同,返回false,如果两个结点都为空,说明相同,返回true,如果当前两个节点的值不相同,返回false,然后再递归比较他们的左右子树。

图解分析:

完整代码:

class Solution {
    public boolean isSameTree(TreeNode p, TreeNode q) {
        if ((q == null && p != null) || (q != null && p == null)) {
            return false;
        }
        if (q == null && p == null) {
            return true;
        }
        if (p.val != q.val) {
            return false;
        }
        return isSameTree(p.left,q.left) && isSameTree(p.right,q.right);
    }
}

🐕2. 另一颗树的子树。

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

解题思路:判断一棵树是不是另一棵树的子树与判断两棵树是不是相同有些相似,首先得保证主树root不为空,然后判断以当前root为跟节点的两棵树是否相同,而这个代码正是第一题的代码,如果不相同,就递归判断当前节点的左子树是否与subRoot树相同,然后递归判断右子树,如果左右子树都不是,就返回false。

图解分析:

完整代码

class Solution {
    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;
    }
        // 判断两棵树是不是相等
    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;
        }
        if (p.val != q.val) {
            return false;
        }
        // 递归判断左右子树
        return isSameTree(p.left,q.left) && isSameTree(p.right,q.right);
    }
}

🐂3. 二叉树最大深度 

链接:104. 二叉树的最大深度 - 力扣(LeetCode)

解题思路:求一颗二叉树的深度,就是当前根节点的左右子树的最大高度值+1,比如下图这棵二叉树

 

 要求这课二叉树的高度,也就是求根节点的左右子树的最大高度再加当前根节点的高度1,3的左右子树的最大高度值为2,2+1 = 3 所以这棵二叉树的高度为3。

图解分析:

完整代码

class Solution {
    public int maxDepth(TreeNode root) {
        if (root == null) {
            return 0;
        }
        int leftHeight = maxDepth(root.left);
        int rightHeight = maxDepth(root.right);
        return Math.max(leftHeight,rightHeight)+1;
    }
}

🐉4. 判断一颗二叉树是否是平衡二叉树。

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

解题思路:先解释一下什么是平衡二叉树,其实就是一颗二叉树中,每个节点的左右子树的高度差值不超过1,如下图

 由图以及上述分析可知,判断一棵树是不是平衡二叉树,与高度有关,所以我们可以新建一个求高度的方法,与求二叉树高度的方法相似,只是我们这次每次标记一下当前根节点的二叉树是否已经不满足平衡二叉树的条件,每次拿到当前根节点的左右子树的高度,当左右子树的高度都 >= 0(为了判断是否满足完全二叉树) 以及 高度差值小于 2 时返回当前根节点的二叉的高度,否则的话就返回 -1 (用来标记已经不满足平衡二叉树)。

图解分析:

是平衡二叉树

 不是平衡二叉树

 所以说,在判断是否是平衡二叉树的方法中,我们只需要判断最后的返回值是否 >= 0 即可。

完整代码

class Solution {
    public boolean isBalanced(TreeNode root) {
      return getHeight(root) >= 0;
   }
    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) < 2) {
            return Math.max(leftHeight,rightHeight) + 1;
        } else {
            return -1;
        }
    }
}

🐭5. 对称二叉树。

链接:101. 对称二叉树 - 力扣(LeetCode)

解题思路:判断一棵树是不是对称二叉树也就是判断这棵树根节点的左右子树是不是对称,而对称指的就是该左子树的左孩子节点是不是与右子树的右孩子节点相同,左子树的右孩子节点是否和右子树的左子节点相同,这样想来,其实与判断两棵树是否相同异曲同工,所以说我们需要写一个判断左右子树是否相等的方法。最后直接返回这个结果即可。

图解分析:

完整代码

// 核心思路:判断根节点左子树的左子树和根节点的右子树的右子树是否相等
          // 以及根节点的左子树的右子树和根节点的右子树的左子树是否相等
class Solution {
    public boolean isSymmetric(TreeNode root) {
        if (root == null) {
            return true;
        }
        return isSymmetricChild(root.left,root.right);
    }
    // 判断两棵树是否相等
    public boolean isSymmetricChild(TreeNode leftNode,TreeNode rightNode) {
        if ((leftNode == null && rightNode != null) || (leftNode != null && rightNode == null)) {
            return false;
        }
        if ((rightNode == null) && (leftNode == null)) {
            return true;
        }
        // 判断值
        if (leftNode.val != rightNode.val) {
            return false;
        }
        // 再判断leftNode的左子树和rightNode的右子树和leftNode的右子树和rightNode的左子树是否相等
        return isSymmetricChild(leftNode.left,rightNode.right) &&
         isSymmetricChild(leftNode.right,rightNode.left);
    }
}

🐷6. 二叉树的构建及遍历。

链接:二叉树遍历_牛客题霸_牛客网 (nowcoder.com)

解题思路:首先需要创建一个节点类

class TreeNode {

    public char val;

    public TreeNode left;

    public TreeNode right;

    public TreeNode(char val) {

        this.val = val;

    }

}

其次,需要注意,使用nextLine接受字符串,还需要一个中序遍历的方法

    // 中序遍历

    public static  void inOrder(TreeNode root) {

        if (root == null) {

            return;

        }

        inOrder(root.left);

        System.out.print(root.val+" ");

        inOrder(root.right);

    }

接下来就是最关键的创建二叉树了,显而易见,该方法的参数就是读取到的字符串,返回值就是节点,需要清楚一点,要对该字符串进行遍历,所以我们需要一个下标 i 来进行遍历,但是该下标会一直增加,直到遍历完字符串,所以将它创建在方法内部显然是不合理的,所以创建为静态变量,因为要将非'#'字母当做节点,所以说,我们分情况讨论,如果当前i下标处的字符不是'#',就创建结点root,创建好后,让i++,然后再递归为当前root节点创建左子节点和右子节点,如果是'#'直接让i++跳过即可,最后返回这个节点root

图解分析:

完整代码

import java.util.Scanner;
class TreeNode {
    public char val;
    public TreeNode left;
    public TreeNode right;
    public TreeNode(char val) {
        this.val = val;
    }
}
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        // 注意 hasNext 和 hasNextLine 的区别
        while (in.hasNextLine()) { // 注意 while 处理多个 case
            String str = in.nextLine();
            TreeNode root = createTree(str);
            inOrder(root);
          
        }
    }
    // 中序遍历
    public static  void inOrder(TreeNode root) {
        if (root == null) {
            return;
        }
        inOrder(root.left);
        System.out.print(root.val+" ");
        inOrder(root.right);
    }
    public static int i;// 字符串下标
    // 创建一颗二叉树
    public static TreeNode createTree(String str) {
        TreeNode root = null;
        if (str.charAt(i) != '#') {
            // 字符,创建节点
            root = new TreeNode(str.charAt(i));
            i++;
            root.left = createTree(str);
            root.right = createTree(str);
        } else {
            i++;
        }
        return root;
    }
}

🐯7. 二叉树的分层遍历 。

链接:102. 二叉树的层序遍历 - 力扣(LeetCode)

解题思路:该oj题的难点在于,如何保存每次的节点值想要拿到每层的节点,可以使用队列先将根节点存入队列,然后判断队列是否为空,不为空的话,就取出队头节点数据,然后判断该节点的左右子树是否为空,不为空就入队列。这样我们就可以进行层序遍历。

但问题在于,如果存放每层的节点呢?

其实,将节点添加进队列时,当队列不为空时,记录此时队列中的数据个数为size,同时创建一个集合temp用来存放节点值,当size>0时,取出队头数据,size--,然后将队头数据的val值加入集合,在判断取出的节点的左右子树是否需要添加,当size == 0 循环结束后,此时集合temp中存放的就是每次的节点值,直接添加到最后返回的集合中。

图解分析:

完整代码

 // 核心思路:通过队列保存每层的数据
class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> list = new ArrayList<>();
         if (root == null) {
            return list;
        }
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        while (!queue.isEmpty()) {
            int size = queue.size();
            List<Integer> temp = new ArrayList<>();
            while (size > 0) {
                // 取出队头数据
                TreeNode cur = queue.poll();
                size--;
                if (cur.left != null) {
                    queue.offer(cur.left);
                }
                if (cur.right != null) {
                    queue.offer(cur.right);
                }
                temp.add(cur.val);
            }
            // 当循环结束后说明一层的数据都放在了temp中
            list.add(temp);
        }
        return list;
    }
}

🐑8. 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先 。

链接:236. 二叉树的最近公共祖先 - 力扣(LeetCode)

解题思路:首先我们需要清楚,找最近公共祖先也就是找这两个节点的所有祖先节点第一次相同时的那个节点,我们可以将其化解成子问题,先判断当前根节点是否等于这两个节点其中一个,如果是,就返回根节点,如果不是的话,就向根节点左子树递归,再向根节点的右子树递归,其最终得到的结果无非三种情况,这两个节点分居根节点两侧,在根节点左子树,以及在根节点右子树,然后我们可以判断递归得到的结果是否为空确定最近最近公共祖先。

图解分析:

以p = 2   q = 5 为例

完整代码

 class Solution {
     public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if (root == null) {
            return null;
        }
        if (root == p || root == q) {
            return root;
        }
        // 递归去找左子树和右子树
        TreeNode leftTree = lowestCommonAncestor(root.left,p,q);
        TreeNode rightTree = lowestCommonAncestor(root.right,p,q);
        if (leftTree != null && rightTree != null) {
            return root;
        } else if (leftTree != null) {
            return leftTree;
        } else if (rightTree != null) {
            return rightTree;
        } else {
            return null;
        }
    }
}

🐍9. 二叉搜索树转换成排序双向链表。

链接:二叉搜索树与双向链表_牛客题霸_牛客网 (nowcoder.com)

解题思路:双向链表有两个指针域,而二叉树也有两个指针域,所以,只需要通过修改二叉树的两个指针域的指向就可以将二叉搜索树转换成双向链表。从根节点的左子树的第一个叶子节点开始调整指向,还需要一个辅助节点prev来帮助转换,如果将prev创建在节点内部,prev将无法保存节点,所以在方法外部创建一个TreeNode类型的成员属性prev,让其保存前一个节点,此时需要修改指向,让当前的叶子节点的left指向prev,接下来的操作应该是prev的right指向当前叶子节点,但是prev的初始值为null,所以说,只有当prev不为空时才可以进行上述操作,然后将当前叶子节点赋给prev,最后递归构建右子树。注:最后要求返回双向链表的头节点,在构建后的双向链表中。只有头节点的left为空,循环找到left为空的节点就是头节点。

图解分析:

完整代码

/**
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;

    }

}
*/
public class Solution {
    public TreeNode prev;
    public TreeNode Convert(TreeNode pRootOfTree) {
        if (pRootOfTree == null) {
            return null;
        }
        createTreee(pRootOfTree);
        while (pRootOfTree.left != null) {
            pRootOfTree = pRootOfTree.left;
        }
        return pRootOfTree;
    }
    public void createTreee(TreeNode root) {
        if (root == null) {
            return;
        }
        createTreee(root.left);
        // 此时root.left为空
        root.left = prev;
        if (prev != null) {
            prev.right = root;
        }
        prev = root;
        createTreee(root.right);
    }
}

🐳10. 根据一棵树的前序遍历与中序遍历构造二叉树。

链接:105. 从前序与中序遍历序列构造二叉树 - 力扣(LeetCode)

解题思路:通过前序遍历可以确定二叉树的根节点,然后再通过中序遍历找到当前根节点的位置,中序遍历中根节点的左侧数据就是左子树,右侧数据就是右子树,所以,可以通过遍历前序遍历的所有节点,然后再通过中序遍历可以构建二叉树。

图解分析:

完整代码:

/**
 * 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 int preIndex = 0;
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        return buildTreeChild(preorder,inorder,0,inorder.length-1);
    }
    private TreeNode buildTreeChild(int[] preorder, int[] inorder,int inbegin,int inend) {
        if (inbegin > inend) {
            return null;
        }
        // 创建一个节点
        TreeNode root = new TreeNode(preorder[preIndex]);
        // 查找当前preIndex下标的节点在中序遍历的位置
        int index = find(preorder[preIndex],inorder,inbegin,inend);
        preIndex++;// 指向下一个根节点
        if (index == -1) {
            return null;
        }
        // 递归构建左右子树
        root.left = buildTreeChild(preorder,inorder,inbegin,index-1);
        root.right = buildTreeChild(preorder,inorder,index+1,inend);
        return root;

    }
    private int find(int val,int[] inorder,int inbegin,int inend) {
        for (int i = inbegin; i <= inend;i++) {
            if (val == inorder[i]) {
                return i;
            }
        }
        return -1;
    }

}

🐇11. 根据一棵树的中序遍历与后序遍历构造二叉树。

链接:106. 从中序与后序遍历序列构造二叉树 - 力扣(LeetCode)

解题思路:通过后序遍历数组可以得到根节点,但是需要注意,与前序遍历不同后序遍历数组的下标从最后一个数据开始向前遍历因为后序遍历是左右根,根节点是最后才遍历到的,先拿到后序遍历数组最后一个数据,然后创建节点,查找中序遍历中此时的根节点值的下标,然后通过划分区间进行根节点的子树构建,但是需要注意的是,先构建右子树,再构建左子树,因为后序遍历是左右根,构建的话是根右左。

图解分析:

完整代码:

/**
 * 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 int postIndex = 0;
    public TreeNode buildTree(int[] inorder, int[] postorder) {
        postIndex = postorder.length - 1;
        return buildTreeChild(inorder,postorder,0,inorder.length - 1);
    }
    public TreeNode buildTreeChild(int[] inorder, int[] postorder,int inbegin,int inend) {
        if (inbegin > inend) {
            return null;
        }
        TreeNode root = new TreeNode(postorder[postIndex]);
        int index = find(postorder[postIndex],inorder,inbegin,inend);
         postIndex--;
        if (index == -1) {
            return null;
        }
        // 递归构建左子树和右子树
        root.right = buildTreeChild(inorder,postorder, index+1,inend);
        root.left = buildTreeChild(inorder,postorder,inbegin,index-1);
        return root;
    }
      // 查找这个节点在中序遍历中的下标
       public int find(int val,int[] inorder,int inbegin,int inend) {
            for (int i = inbegin;i <= inend;i++) {
                if (inorder[i] == val) {
                    return i;
                }
            }
            return -1;
        }
}

🐝12. 二叉树创建字符串。

链接:606. 根据二叉树创建字符串 - 力扣(LeetCode)

解题思路:由于String的拼接会产生很多无用的对象,所以使用StringBuilder来进行拼接,如果结点为空,直接return结束操作即可,首先拼接当前root.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 String tree2str(TreeNode root) {
        StringBuilder stringBuilder = new StringBuilder();
        tree2strChild(root,stringBuilder);
        return stringBuilder.toString();
    }
    public void tree2strChild(TreeNode root, StringBuilder stringBuilder) {
        if (root == null) {
            return;
        }
        // 首先进行拼接当前值
        stringBuilder.append(root.val);
        if (root.left != null) {
            stringBuilder.append("(");
            tree2strChild(root.left,stringBuilder);
            stringBuilder.append(")");
        } else {
            if (root.right == null) {
                return;
            } else {
                stringBuilder.append("()");
            }
        }
        if (root.right != null) {
            stringBuilder.append("(");
            tree2strChild(root.right,stringBuilder);
            stringBuilder.append(")");
        } else {
            return;
        }
    }
}

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值